Test - CF per XBOX

This commit is contained in:
Alhaziel
2019-12-03 18:11:56 +01:00
parent 3b533d4ef9
commit ef822e749d
208 changed files with 115980 additions and 78 deletions
View File
-929
View File
@@ -1,929 +0,0 @@
from __future__ import unicode_literals
import re
import datetime
from .desc import *
from .simplex import *
from .conversions import *
from pyjsparser import PyJsParser
import six
if six.PY2:
from itertools import izip
else:
izip = zip
def Type(obj):
return obj.TYPE
# 8.6.2
class PyJs(object):
TYPE = 'Object'
IS_CONSTRUCTOR = False
prototype = None
Class = None
extensible = True
value = None
own = {}
def get_member(self, unconverted_prop):
return self.get(to_string(unconverted_prop))
def put_member(self, unconverted_prop, val):
return self.put(to_string(unconverted_prop), val)
def get(self, prop):
assert type(prop) == unicode
cand = self.get_property(prop)
if cand is None:
return undefined
if is_data_descriptor(cand):
return cand['value']
if is_undefined(cand['get']):
return undefined
return cand['get'].call(self)
def get_own_property(self, prop):
assert type(prop) == unicode
# takes py returns py
return self.own.get(prop)
def get_property(self, prop):
assert type(prop) == unicode
# take py returns py
cand = self.get_own_property(prop)
if cand:
return cand
if self.prototype is not None:
return self.prototype.get_property(prop)
def put(self, prop, val, throw=False):
assert type(prop) == unicode
# takes py, returns none
if not self.can_put(prop):
if throw:
raise MakeError('TypeError', 'Could not define own property')
return
own_desc = self.get_own_property(prop)
if is_data_descriptor(own_desc):
self.own[prop]['value'] = val
return
desc = self.get_property(prop)
if is_accessor_descriptor(desc):
desc['set'].call(
self, (val, )) # calling setter on own or inherited element
else: # new property
self.own[prop] = {
'value': val,
'writable': True,
'configurable': True,
'enumerable': True
}
def can_put(self, prop): # to check
assert type(prop) == unicode, type(prop)
# takes py returns py
desc = self.get_own_property(prop)
if desc: # if we have this property
if is_accessor_descriptor(desc):
return is_callable(
desc['set']) # Check if setter method is defined
else: # data desc
return desc['writable']
if self.prototype is None:
return self.extensible
inherited = self.prototype.get_property(prop)
if inherited is None:
return self.extensible
if is_accessor_descriptor(inherited):
return not is_undefined(inherited['set'])
elif self.extensible:
return inherited['writable'] # weird...
return False
def has_property(self, prop):
assert type(prop) == unicode
# takes py returns Py
return self.get_property(prop) is not None
def delete(self, prop, throw=False):
assert type(prop) == unicode
# takes py, returns py
desc = self.get_own_property(prop)
if desc is None:
return True
if desc['configurable']:
del self.own[prop]
return True
if throw:
raise MakeError('TypeError', 'Could not define own property')
return False
def default_value(self, hint=None):
order = ('valueOf', 'toString')
if hint == 'String' or (hint is None and self.Class == 'Date'):
order = ('toString', 'valueOf')
for meth_name in order:
method = self.get(meth_name)
if method is not None and is_callable(method):
cand = method.call(self, ())
if is_primitive(cand):
return cand
raise MakeError('TypeError',
'Cannot convert object to primitive value')
def define_own_property(
self, prop, desc,
throw): # Internal use only. External through Object
assert type(prop) == unicode
# takes Py, returns Py
# prop must be a Py string. Desc is either a descriptor or accessor.
# Messy method - raw translation from Ecma spec to prevent any bugs. # todo check this
current = self.get_own_property(prop)
extensible = self.extensible
if not current: # We are creating a new OWN property
if not extensible:
if throw:
raise MakeError('TypeError',
'Could not define own property')
return False
# extensible must be True
if is_data_descriptor(desc) or is_generic_descriptor(desc):
DEFAULT_DATA_DESC = {
'value': undefined, # undefined
'writable': False,
'enumerable': False,
'configurable': False
}
DEFAULT_DATA_DESC.update(desc)
self.own[prop] = DEFAULT_DATA_DESC
else:
DEFAULT_ACCESSOR_DESC = {
'get': undefined, # undefined
'set': undefined, # undefined
'enumerable': False,
'configurable': False
}
DEFAULT_ACCESSOR_DESC.update(desc)
self.own[prop] = DEFAULT_ACCESSOR_DESC
return True
# therefore current exists!
if not desc or desc == current: # We don't need to change anything.
return True
configurable = current['configurable']
if not configurable: # Prevent changing params
if desc.get('configurable'):
if throw:
raise MakeError('TypeError',
'Could not define own property')
return False
if 'enumerable' in desc and desc['enumerable'] != current[
'enumerable']:
if throw:
raise MakeError('TypeError',
'Could not define own property')
return False
if is_generic_descriptor(desc):
pass
elif is_data_descriptor(current) != is_data_descriptor(desc):
# we want to change the current type of property
if not configurable:
if throw:
raise MakeError('TypeError',
'Could not define own property')
return False
if is_data_descriptor(current): # from data to setter
del current['value']
del current['writable']
current['set'] = undefined # undefined
current['get'] = undefined # undefined
else: # from setter to data
del current['set']
del current['get']
current['value'] = undefined # undefined
current['writable'] = False
elif is_data_descriptor(current) and is_data_descriptor(desc):
if not configurable:
if not current['writable'] and desc.get('writable'):
if throw:
raise MakeError('TypeError',
'Could not define own property')
return False
if not current['writable'] and 'value' in desc and current[
'value'] != desc['value']:
if throw:
raise MakeError('TypeError',
'Could not define own property')
return False
elif is_accessor_descriptor(current) and is_accessor_descriptor(desc):
if not configurable:
if 'set' in desc and desc['set'] != current['set']:
if throw:
raise MakeError('TypeError',
'Could not define own property')
return False
if 'get' in desc and desc['get'] != current['get']:
if throw:
raise MakeError('TypeError',
'Could not define own property')
return False
current.update(desc)
return True
def create(self, args, space):
'''Generally not a constructor, raise an error'''
raise MakeError('TypeError', '%s is not a constructor' % self.Class)
def get_member(
self, prop, space
): # general member getter, prop has to be unconverted prop. it is it can be any value
typ = type(self)
if typ not in PRIMITIVES: # most likely getter for object
return self.get_member(
prop
) # <- object can implement this to support faster prop getting. ex array.
elif typ == unicode: # then probably a String
if type(prop) == float and is_finite(prop):
index = int(prop)
if index == prop and 0 <= index < len(self):
return self[index]
s_prop = to_string(prop)
if s_prop == 'length':
return float(len(self))
elif s_prop.isdigit():
index = int(s_prop)
if 0 <= index < len(self):
return self[index]
# use standard string prototype
return space.StringPrototype.get(s_prop)
# maybe an index
elif typ == float:
# use standard number prototype
return space.NumberPrototype.get(to_string(prop))
elif typ == bool:
return space.BooleanPrototype.get(to_string(prop))
elif typ is UNDEFINED_TYPE:
raise MakeError('TypeError',
"Cannot read property '%s' of undefined" % prop)
elif typ is NULL_TYPE:
raise MakeError('TypeError',
"Cannot read property '%s' of null" % prop)
else:
raise RuntimeError('Unknown type! - ' + repr(typ))
def get_member_dot(self, prop, space):
# dot member getter, prop has to be unicode
typ = type(self)
if typ not in PRIMITIVES: # most likely getter for object
return self.get(prop)
elif typ == unicode: # then probably a String
if prop == 'length':
return float(len(self))
elif prop.isdigit():
index = int(prop)
if 0 <= index < len(self):
return self[index]
else:
# use standard string prototype
return space.StringPrototype.get(prop)
# maybe an index
elif typ == float:
# use standard number prototype
return space.NumberPrototype.get(prop)
elif typ == bool:
return space.BooleanPrototype.get(prop)
elif typ in (UNDEFINED_TYPE, NULL_TYPE):
raise MakeError('TypeError',
"Cannot read property '%s' of undefined" % prop)
else:
raise RuntimeError('Unknown type! - ' + repr(typ))
# Object
class PyJsObject(PyJs):
TYPE = 'Object'
Class = 'Object'
def __init__(self, prototype=None):
self.prototype = prototype
self.own = {}
def _init(self, props, vals):
i = 0
for prop, kind in props:
if prop in self.own: # just check... probably will not happen very often.
if is_data_descriptor(self.own[prop]):
if kind != 'i':
raise MakeError(
'SyntaxError',
'Invalid object initializer! Duplicate property name "%s"'
% prop)
else:
if kind == 'i' or (kind == 'g' and 'get' in self.own[prop]
) or (kind == 's'
and 'set' in self.own[prop]):
raise MakeError(
'SyntaxError',
'Invalid object initializer! Duplicate setter/getter of prop: "%s"'
% prop)
if kind == 'i': # init
self.own[prop] = {
'value': vals[i],
'writable': True,
'enumerable': True,
'configurable': True
}
elif kind == 'g': # get
self.define_own_property(prop, {
'get': vals[i],
'enumerable': True,
'configurable': True
}, False)
elif kind == 's': # get
self.define_own_property(prop, {
'get': vals[i],
'enumerable': True,
'configurable': True
}, False)
else:
raise RuntimeError(
'Invalid property kind - %s. Expected one of i, g, s.' %
repr(kind))
i += 1
def _set_props(self, prop_descs):
for prop, desc in six.iteritems(prop_descs):
self.define_own_property(prop, desc)
# Array
# todo Optimise Array - extremely slow due to index conversions from str to int and back etc.
# solution - make get and put methods callable with any type of prop and handle conversions from inside
# if not array then use to_string(prop). In array if prop is integer then just use it
# also consider removal of these stupid writable, enumerable etc for ints.
class PyJsArray(PyJs):
Class = 'Array'
def __init__(self, length, prototype=None):
self.prototype = prototype
self.own = {
'length': {
'value': float(length),
'writable': True,
'enumerable': False,
'configurable': False
}
}
def _init(self, elements):
for i, ele in enumerate(elements):
if ele is None: continue
self.own[unicode(i)] = {
'value': ele,
'writable': True,
'enumerable': True,
'configurable': True
}
def put(self, prop, val, throw=False):
assert type(val) != int
# takes py, returns none
if not self.can_put(prop):
if throw:
raise MakeError('TypeError', 'Could not define own property')
return
own_desc = self.get_own_property(prop)
if is_data_descriptor(own_desc):
self.define_own_property(prop, {'value': val}, False)
return
desc = self.get_property(prop)
if is_accessor_descriptor(desc):
desc['set'].call(
self, (val, )) # calling setter on own or inherited element
else: # new property
self.define_own_property(
prop, {
'value': val,
'writable': True,
'configurable': True,
'enumerable': True
}, False)
def define_own_property(self, prop, desc, throw):
assert type(desc.get('value')) != int
old_len_desc = self.get_own_property('length')
old_len = old_len_desc['value'] # value is js type so convert to py.
if prop == 'length':
if 'value' not in desc:
return PyJs.define_own_property(self, prop, desc, False)
new_len = to_uint32(desc['value'])
if new_len != to_number(desc['value']):
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = float(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc, False)
if not old_len_desc['writable']:
return False
if 'writable' not in new_desc or new_desc['writable'] == True:
new_writable = True
else:
new_writable = False
new_desc['writable'] = True
if not PyJs.define_own_property(self, prop, new_desc, False):
return False
if new_len < old_len:
# not very efficient for sparse arrays, so using different method for sparse:
if old_len > 30 * len(self.own):
for ele in self.own.keys():
if ele.isdigit() and int(ele) >= new_len:
if not self.delete(
ele
): # if failed to delete set len to current len and reject.
new_desc['value'] = old_len + 1.
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(
self, prop, new_desc, False)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
unicode(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = old_len + 1.
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc,
False)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = to_uint32(prop)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc, False):
return False
if index >= old_len:
old_len_desc['value'] = index + 1.
return True
else:
return PyJs.define_own_property(self, prop, desc, False)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
# database with compiled patterns. Js pattern -> Py pattern.
REGEXP_DB = {}
class PyJsRegExp(PyJs):
Class = 'RegExp'
def __init__(self, body, flags, prototype=None):
self.prototype = prototype
self.glob = True if 'g' in flags else False
self.ignore_case = re.IGNORECASE if 'i' in flags else 0
self.multiline = re.MULTILINE if 'm' in flags else 0
self.value = body
if (body, flags) in REGEXP_DB:
self.pat = REGEXP_DB[body, flags]
else:
comp = None
try:
# converting JS regexp pattern to Py pattern.
possible_fixes = [(u'[]', u'[\0]'), (u'[^]', u'[^\0]'),
(u'nofix1791', u'nofix1791')]
reg = self.value
for fix, rep in possible_fixes:
comp = PyJsParser()._interpret_regexp(reg, flags)
#print 'reg -> comp', reg, '->', comp
try:
self.pat = re.compile(
comp, self.ignore_case | self.multiline)
#print reg, '->', comp
break
except:
reg = reg.replace(fix, rep)
# print 'Fix', fix, '->', rep, '=', reg
else:
raise Exception()
REGEXP_DB[body, flags] = self.pat
except:
#print 'Invalid pattern...', self.value, comp
raise MakeError(
'SyntaxError',
'Invalid RegExp pattern: %s -> %s' % (repr(self.value),
repr(comp)))
# now set own properties:
self.own = {
'source': {
'value': self.value,
'enumerable': False,
'writable': False,
'configurable': False
},
'global': {
'value': self.glob,
'enumerable': False,
'writable': False,
'configurable': False
},
'ignoreCase': {
'value': bool(self.ignore_case),
'enumerable': False,
'writable': False,
'configurable': False
},
'multiline': {
'value': bool(self.multiline),
'enumerable': False,
'writable': False,
'configurable': False
},
'lastIndex': {
'value': 0.,
'enumerable': False,
'writable': True,
'configurable': False
}
}
def match(self, string, pos):
'''string is of course a py string'''
return self.pat.match(string, int(pos))
class PyJsError(PyJs):
Class = 'Error'
extensible = True
def __init__(self, message=None, prototype=None):
self.prototype = prototype
self.own = {}
if message is not None:
self.put('message', to_string(message))
self.own['message']['enumerable'] = False
class PyJsDate(PyJs):
Class = 'Date'
UTCToLocal = None # todo UTC to local should be imported!
def __init__(self, value, prototype=None):
self.value = value
self.own = {}
self.prototype = prototype
# todo fix this problematic datetime part
def to_local_dt(self):
return datetime.datetime.utcfromtimestamp(
self.UTCToLocal(self.value) // 1000)
def to_utc_dt(self):
return datetime.datetime.utcfromtimestamp(self.value // 1000)
def local_strftime(self, pattern):
if self.value is NaN:
return 'Invalid Date'
try:
dt = self.to_local_dt()
except:
raise MakeError(
'TypeError',
'unsupported date range. Will fix in future versions')
try:
return dt.strftime(pattern)
except:
raise MakeError(
'TypeError',
'Could not generate date string from this date (limitations of python.datetime)'
)
def utc_strftime(self, pattern):
if self.value is NaN:
return 'Invalid Date'
try:
dt = self.to_utc_dt()
except:
raise MakeError(
'TypeError',
'unsupported date range. Will fix in future versions')
try:
return dt.strftime(pattern)
except:
raise MakeError(
'TypeError',
'Could not generate date string from this date (limitations of python.datetime)'
)
# Scope class it will hold all the variables accessible to user
class Scope(PyJs):
Class = 'Global'
extensible = True
IS_CHILD_SCOPE = True
THIS_BINDING = None
space = None
exe = None
# todo speed up!
# in order to speed up this very important class the top scope should behave differently than
# child scopes, child scope should not have this property descriptor thing because they cant be changed anyway
# they are all confugurable= False
def __init__(self, scope, space, parent=None):
"""Doc"""
self.space = space
self.prototype = parent
if type(scope) is not dict:
assert parent is not None, 'You initialised the WITH_SCOPE without a parent scope.'
self.own = scope
self.is_with_scope = True
else:
self.is_with_scope = False
if parent is None:
# global, top level scope
self.own = {}
for k, v in six.iteritems(scope):
# set all the global items
self.define_own_property(
k, {
'value': v,
'configurable': False,
'writable': False,
'enumerable': False
}, False)
else:
# not global, less powerful but faster closure.
self.own = scope # simple dictionary which maps name directly to js object.
self.par = super(Scope, self)
self.stack = []
def register(self, var):
# registered keeps only global registered variables
if self.prototype is None:
# define in global scope
if var in self.own:
self.own[var]['configurable'] = False
else:
self.define_own_property(
var, {
'value': undefined,
'configurable': False,
'writable': True,
'enumerable': True
}, False)
elif var not in self.own:
# define in local scope since it has not been defined yet
self.own[var] = undefined # default value
def registers(self, vars):
"""register multiple variables"""
for var in vars:
self.register(var)
def put(self, var, val, throw=False):
if self.prototype is None:
desc = self.own.get(var) # global scope
if desc is None:
self.par.put(var, val, False)
else:
if desc['writable']: # todo consider getters/setters
desc['value'] = val
else:
if self.is_with_scope:
if self.own.has_property(var):
return self.own.put(var, val, throw=throw)
else:
return self.prototype.put(var, val)
# trying to put in local scope
# we dont know yet in which scope we should place this var
elif var in self.own:
self.own[var] = val
return val
else:
# try to put in the lower scope since we cant put in this one (var wasn't registered)
return self.prototype.put(var, val)
def get(self, var, throw=False):
if self.prototype is not None:
if self.is_with_scope:
cand = None if not self.own.has_property(
var) else self.own.get(var)
else:
# fast local scope
cand = self.own.get(var)
if cand is None:
return self.prototype.get(var, throw)
return cand
# slow, global scope
if var not in self.own:
# try in ObjectPrototype...
if var in self.space.ObjectPrototype.own:
return self.space.ObjectPrototype.get(var)
if throw:
raise MakeError('ReferenceError', '%s is not defined' % var)
return undefined
cand = self.own[var].get('value')
return cand if cand is not None else self.own[var]['get'].call(self)
def delete(self, var, throw=False):
if self.prototype is not None:
if self.is_with_scope:
if self.own.has_property(var):
return self.own.delete(var)
elif var in self.own:
return False
return self.prototype.delete(var)
# we are in global scope here. Must exist and be configurable to delete
if var not in self.own:
# this var does not exist, why do you want to delete it???
return True
if self.own[var]['configurable']:
del self.own[var]
return True
# not configurable, cant delete
return False
def get_new_arguments_obj(args, space):
obj = space.NewObject()
obj.Class = 'Arguments'
obj.define_own_property(
'length', {
'value': float(len(args)),
'writable': True,
'enumerable': False,
'configurable': True
}, False)
for i, e in enumerate(args):
obj.put(unicode(i), e)
return obj
#Function
class PyJsFunction(PyJs):
Class = 'Function'
source = '{ [native code] }'
IS_CONSTRUCTOR = True
def __init__(self,
code,
ctx,
params,
name,
space,
is_declaration,
definitions,
prototype=None):
self.prototype = prototype
self.own = {}
self.code = code
if type(
self.code
) == int: # just a label pointing to the beginning of the code.
self.is_native = False
else:
self.is_native = True # python function
self.ctx = ctx
self.params = params
self.arguments_in_params = 'arguments' in params
self.definitions = definitions
# todo remove this check later
for p in params:
assert p in self.definitions
self.name = name
self.space = space
self.is_declaration = is_declaration
#set own property length to the number of arguments
self.own['length'] = {
'value': float(len(params)),
'writable': False,
'enumerable': False,
'configurable': False
}
if name:
self.own['name'] = {
'value': name,
'writable': False,
'enumerable': False,
'configurable': True
}
if not self.is_native: # set prototype for user defined functions
# constructor points to this function
proto = space.NewObject()
proto.own['constructor'] = {
'value': self,
'writable': True,
'enumerable': False,
'configurable': True
}
self.own['prototype'] = {
'value': proto,
'writable': True,
'enumerable': False,
'configurable': False
}
# todo set up throwers on callee and arguments if in strict mode
def call(self, this, args=()):
''' Dont use this method from inside bytecode to call other bytecode. '''
if self.is_native:
_args = SpaceTuple(
args
) # we have to do that unfortunately to pass all the necessary info to the funcs
_args.space = self.space
return self.code(
this, _args
) # must return valid js object - undefined, null, float, unicode, bool, or PyJs
else:
return self.space.exe._call(self, this,
args) # will run inside bytecode
def has_instance(self, other):
# I am not sure here so instanceof may not work lol.
if not is_object(other):
return False
proto = self.get('prototype')
if not is_object(proto):
raise MakeError(
'TypeError',
'Function has non-object prototype in instanceof check')
while True:
other = other.prototype
if not other: # todo make sure that the condition is not None or null
return False
if other is proto:
return True
def create(self, args, space):
proto = self.get('prototype')
if not is_object(proto):
proto = space.ObjectPrototype
new = PyJsObject(prototype=proto)
res = self.call(new, args)
if is_object(res):
return res
return new
def _generate_my_context(self, this, args):
my_ctx = Scope(
dict(izip(self.params, args)), self.space, parent=self.ctx)
my_ctx.registers(self.definitions)
my_ctx.THIS_BINDING = this
if not self.arguments_in_params:
my_ctx.own['arguments'] = get_new_arguments_obj(args, self.space)
if not self.is_declaration and self.name and self.name not in my_ctx.own:
my_ctx.own[
self.
name] = self # this should be immutable binding but come on!
return my_ctx
class SpaceTuple:
def __init__(self, tup):
self.tup = tup
def __len__(self):
return len(self.tup)
def __getitem__(self, item):
return self.tup[item]
def __iter__(self):
return iter(self.tup)
-753
View File
@@ -1,753 +0,0 @@
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()
-227
View File
@@ -1,227 +0,0 @@
from .opcodes import *
from .space import *
from .base import *
class Code:
'''Can generate, store and run sequence of ops representing js code'''
def __init__(self, is_strict=False, debug_mode=False):
self.tape = []
self.compiled = False
self.label_locs = None
self.is_strict = is_strict
self.debug_mode = debug_mode
self.contexts = []
self.current_ctx = None
self.return_locs = []
self._label_count = 0
self.label_locs = None
# useful references
self.GLOBAL_THIS = None
self.space = None
# dbg
self.ctx_depth = 0
def get_new_label(self):
self._label_count += 1
return self._label_count
def emit(self, op_code, *args):
''' Adds op_code with specified args to tape '''
self.tape.append(OP_CODES[op_code](*args))
def compile(self, start_loc=0):
''' Records locations of labels and compiles the code '''
self.label_locs = {} if self.label_locs is None else self.label_locs
loc = start_loc
while loc < len(self.tape):
if type(self.tape[loc]) == LABEL:
self.label_locs[self.tape[loc].num] = loc
del self.tape[loc]
continue
loc += 1
self.compiled = True
def _call(self, func, this, args):
''' Calls a bytecode function func
NOTE: use !ONLY! when calling functions from native methods! '''
assert not func.is_native
# fake call - the the runner to return to the end of the file
old_contexts = self.contexts
old_return_locs = self.return_locs
old_curr_ctx = self.current_ctx
self.contexts = [FakeCtx()]
self.return_locs = [len(self.tape)] # target line after return
# prepare my ctx
my_ctx = func._generate_my_context(this, args)
self.current_ctx = my_ctx
# execute dunction
ret = self.run(my_ctx, starting_loc=self.label_locs[func.code])
# bring back old execution
self.current_ctx = old_curr_ctx
self.contexts = old_contexts
self.return_locs = old_return_locs
return ret
def execute_fragment_under_context(self, ctx, start_label, end_label):
''' just like run but returns if moved outside of the specified fragment
# 4 different exectution results
# 0=normal, 1=return, 2=jump_outside, 3=errors
# execute_fragment_under_context returns:
# (return_value, typ, return_value/jump_loc/py_error)
# IMPARTANT: It is guaranteed that the length of the ctx.stack is unchanged.
'''
old_curr_ctx = self.current_ctx
self.ctx_depth += 1
old_stack_len = len(ctx.stack)
old_ret_len = len(self.return_locs)
old_ctx_len = len(self.contexts)
try:
self.current_ctx = ctx
return self._execute_fragment_under_context(
ctx, start_label, end_label)
except JsException as err:
if self.debug_mode:
self._on_fragment_exit("js errors")
# undo the things that were put on the stack (if any) to ensure a proper error recovery
del ctx.stack[old_stack_len:]
del self.return_locs[old_ret_len:]
del self.contexts[old_ctx_len :]
return undefined, 3, err
finally:
self.ctx_depth -= 1
self.current_ctx = old_curr_ctx
assert old_stack_len == len(ctx.stack)
def _get_dbg_indent(self):
return self.ctx_depth * ' '
def _on_fragment_exit(self, mode):
print(self._get_dbg_indent() + 'ctx exit (%s)' % mode)
def _execute_fragment_under_context(self, ctx, start_label, end_label):
start, end = self.label_locs[start_label], self.label_locs[end_label]
initial_len = len(ctx.stack)
loc = start
entry_level = len(self.contexts)
# for e in self.tape[start:end]:
# print e
if self.debug_mode:
print(self._get_dbg_indent() + 'ctx entry (from:%d, to:%d)' % (start, end))
while loc < len(self.tape):
if len(self.contexts) == entry_level and loc >= end:
if self.debug_mode:
self._on_fragment_exit('normal')
assert loc == end
delta_stack = len(ctx.stack) - initial_len
assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack
return ctx.stack.pop(), 0, None # means normal return
# execute instruction
if self.debug_mode:
print(self._get_dbg_indent() + str(loc), self.tape[loc])
status = self.tape[loc].eval(ctx)
# check status for special actions
if status is not None:
if type(status) == int: # jump to label
loc = self.label_locs[status]
if len(self.contexts) == entry_level:
# check if jumped outside of the fragment and break if so
if not start <= loc < end:
if self.debug_mode:
self._on_fragment_exit('jump outside loc:%d label:%d' % (loc, status))
delta_stack = len(ctx.stack) - initial_len
assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack
return ctx.stack.pop(), 2, status # jump outside
continue
elif len(status) == 2: # a call or a return!
# call: (new_ctx, func_loc_label_num)
if status[0] is not None:
# append old state to the stack
self.contexts.append(ctx)
self.return_locs.append(loc + 1)
# set new state
loc = self.label_locs[status[1]]
ctx = status[0]
self.current_ctx = ctx
continue
# return: (None, None)
else:
if len(self.contexts) == entry_level:
if self.debug_mode:
self._on_fragment_exit('return')
delta_stack = len(ctx.stack) - initial_len
assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack
return undefined, 1, ctx.stack.pop(
) # return signal
return_value = ctx.stack.pop()
ctx = self.contexts.pop()
self.current_ctx = ctx
ctx.stack.append(return_value)
loc = self.return_locs.pop()
continue
# next instruction
loc += 1
if self.debug_mode:
self._on_fragment_exit('internal error - unexpected end of tape, will crash')
assert False, 'Remember to add NOP at the end!'
def run(self, ctx, starting_loc=0):
loc = starting_loc
self.current_ctx = ctx
while loc < len(self.tape):
# execute instruction
if self.debug_mode:
print(loc, self.tape[loc])
status = self.tape[loc].eval(ctx)
# check status for special actions
if status is not None:
if type(status) == int: # jump to label
loc = self.label_locs[status]
continue
elif len(status) == 2: # a call or a return!
# call: (new_ctx, func_loc_label_num)
if status[0] is not None:
# append old state to the stack
self.contexts.append(ctx)
self.return_locs.append(loc + 1)
# set new state
loc = self.label_locs[status[1]]
ctx = status[0]
self.current_ctx = ctx
continue
# return: (None, None)
else:
return_value = ctx.stack.pop()
ctx = self.contexts.pop()
self.current_ctx = ctx
ctx.stack.append(return_value)
loc = self.return_locs.pop()
continue
# next instruction
loc += 1
assert len(ctx.stack) == 1, ctx.stack
return ctx.stack.pop()
class FakeCtx(object):
def __init__(self):
self.stack = []
@@ -1 +0,0 @@
__author__ = 'Piotr Dabkowski'
@@ -1,28 +0,0 @@
from ..conversions import *
from ..func_utils import *
def Array(this, args):
return ArrayConstructor(args, args.space)
def ArrayConstructor(args, space):
if len(args) == 1:
l = get_arg(args, 0)
if type(l) == float:
if to_uint32(l) == l:
return space.NewArray(l)
else:
raise MakeError(
'RangeError',
'Invalid length specified for Array constructor (must be uint32)'
)
else:
return space.ConstructArray([l])
else:
return space.ConstructArray(list(args))
def isArray(this, args):
x = get_arg(args, 0)
return is_object(x) and x.Class == u'Array'
@@ -1,14 +0,0 @@
from ..conversions import *
from ..func_utils import *
def Boolean(this, args):
return to_boolean(get_arg(args, 0))
def BooleanConstructor(args, space):
temp = space.NewObject()
temp.prototype = space.BooleanPrototype
temp.Class = 'Boolean'
temp.value = to_boolean(get_arg(args, 0))
return temp
@@ -1,11 +0,0 @@
from __future__ import unicode_literals
from js2py.internals.conversions import *
from js2py.internals.func_utils import *
class ConsoleMethods:
def log(this, args):
x = ' '.join(to_string(e) for e in args)
print(x)
return undefined
-405
View File
@@ -1,405 +0,0 @@
from ..base import *
from .time_helpers import *
TZ_OFFSET = (time.altzone // 3600)
ABS_OFFSET = abs(TZ_OFFSET)
TZ_NAME = time.tzname[1]
ISO_FORMAT = '%s-%s-%sT%s:%s:%s.%sZ'
@Js
def Date(year, month, date, hours, minutes, seconds, ms):
return now().to_string()
Date.Class = 'Date'
def now():
return PyJsDate(int(time.time() * 1000), prototype=DatePrototype)
@Js
def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this
args = arguments
y = args[0].to_number()
m = args[1].to_number()
l = len(args)
dt = args[2].to_number() if l > 2 else Js(1)
h = args[3].to_number() if l > 3 else Js(0)
mi = args[4].to_number() if l > 4 else Js(0)
sec = args[5].to_number() if l > 5 else Js(0)
mili = args[6].to_number() if l > 6 else Js(0)
if not y.is_nan() and 0 <= y.value <= 99:
y = y + Js(1900)
t = TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
return PyJsDate(t, prototype=DatePrototype)
@Js
def parse(string):
return PyJsDate(
TimeClip(parse_date(string.to_string().value)),
prototype=DatePrototype)
Date.define_own_property('now', {
'value': Js(now),
'enumerable': False,
'writable': True,
'configurable': True
})
Date.define_own_property('parse', {
'value': parse,
'enumerable': False,
'writable': True,
'configurable': True
})
Date.define_own_property('UTC', {
'value': UTC,
'enumerable': False,
'writable': True,
'configurable': True
})
class PyJsDate(PyJs):
Class = 'Date'
extensible = True
def __init__(self, value, prototype=None):
self.value = value
self.own = {}
self.prototype = prototype
# todo fix this problematic datetime part
def to_local_dt(self):
return datetime.datetime.utcfromtimestamp(
UTCToLocal(self.value) // 1000)
def to_utc_dt(self):
return datetime.datetime.utcfromtimestamp(self.value // 1000)
def local_strftime(self, pattern):
if self.value is NaN:
return 'Invalid Date'
try:
dt = self.to_local_dt()
except:
raise MakeError(
'TypeError',
'unsupported date range. Will fix in future versions')
try:
return dt.strftime(pattern)
except:
raise MakeError(
'TypeError',
'Could not generate date string from this date (limitations of python.datetime)'
)
def utc_strftime(self, pattern):
if self.value is NaN:
return 'Invalid Date'
try:
dt = self.to_utc_dt()
except:
raise MakeError(
'TypeError',
'unsupported date range. Will fix in future versions')
try:
return dt.strftime(pattern)
except:
raise MakeError(
'TypeError',
'Could not generate date string from this date (limitations of python.datetime)'
)
def parse_date(py_string): # todo support all date string formats
try:
try:
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%S.%fZ")
except:
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%SZ")
return MakeDate(
MakeDay(Js(dt.year), Js(dt.month - 1), Js(dt.day)),
MakeTime(
Js(dt.hour), Js(dt.minute), Js(dt.second),
Js(dt.microsecond // 1000)))
except:
raise MakeError(
'TypeError',
'Could not parse date %s - unsupported date format. Currently only supported format is RFC3339 utc. Sorry!'
% py_string)
def date_constructor(*args):
if len(args) >= 2:
return date_constructor2(*args)
elif len(args) == 1:
return date_constructor1(args[0])
else:
return date_constructor0()
def date_constructor0():
return now()
def date_constructor1(value):
v = value.to_primitive()
if v._type() == 'String':
v = parse_date(v.value)
else:
v = v.to_int()
return PyJsDate(TimeClip(v), prototype=DatePrototype)
def date_constructor2(*args):
y = args[0].to_number()
m = args[1].to_number()
l = len(args)
dt = args[2].to_number() if l > 2 else Js(1)
h = args[3].to_number() if l > 3 else Js(0)
mi = args[4].to_number() if l > 4 else Js(0)
sec = args[5].to_number() if l > 5 else Js(0)
mili = args[6].to_number() if l > 6 else Js(0)
if not y.is_nan() and 0 <= y.value <= 99:
y = y + Js(1900)
t = TimeClip(
LocalToUTC(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili))))
return PyJsDate(t, prototype=DatePrototype)
Date.create = date_constructor
DatePrototype = PyJsDate(float('nan'), prototype=ObjectPrototype)
def check_date(obj):
if obj.Class != 'Date':
raise MakeError('TypeError', 'this is not a Date object')
class DateProto:
def toString():
check_date(this)
if this.value is NaN:
return 'Invalid Date'
offset = (UTCToLocal(this.value) - this.value) // msPerHour
return this.local_strftime(
'%a %b %d %Y %H:%M:%S GMT') + '%s00 (%s)' % (pad(
offset, 2, True), GetTimeZoneName(this.value))
def toDateString():
check_date(this)
return this.local_strftime('%d %B %Y')
def toTimeString():
check_date(this)
return this.local_strftime('%H:%M:%S')
def toLocaleString():
check_date(this)
return this.local_strftime('%d %B %Y %H:%M:%S')
def toLocaleDateString():
check_date(this)
return this.local_strftime('%d %B %Y')
def toLocaleTimeString():
check_date(this)
return this.local_strftime('%H:%M:%S')
def valueOf():
check_date(this)
return this.value
def getTime():
check_date(this)
return this.value
def getFullYear():
check_date(this)
if this.value is NaN:
return NaN
return YearFromTime(UTCToLocal(this.value))
def getUTCFullYear():
check_date(this)
if this.value is NaN:
return NaN
return YearFromTime(this.value)
def getMonth():
check_date(this)
if this.value is NaN:
return NaN
return MonthFromTime(UTCToLocal(this.value))
def getDate():
check_date(this)
if this.value is NaN:
return NaN
return DateFromTime(UTCToLocal(this.value))
def getUTCMonth():
check_date(this)
if this.value is NaN:
return NaN
return MonthFromTime(this.value)
def getUTCDate():
check_date(this)
if this.value is NaN:
return NaN
return DateFromTime(this.value)
def getDay():
check_date(this)
if this.value is NaN:
return NaN
return WeekDay(UTCToLocal(this.value))
def getUTCDay():
check_date(this)
if this.value is NaN:
return NaN
return WeekDay(this.value)
def getHours():
check_date(this)
if this.value is NaN:
return NaN
return HourFromTime(UTCToLocal(this.value))
def getUTCHours():
check_date(this)
if this.value is NaN:
return NaN
return HourFromTime(this.value)
def getMinutes():
check_date(this)
if this.value is NaN:
return NaN
return MinFromTime(UTCToLocal(this.value))
def getUTCMinutes():
check_date(this)
if this.value is NaN:
return NaN
return MinFromTime(this.value)
def getSeconds():
check_date(this)
if this.value is NaN:
return NaN
return SecFromTime(UTCToLocal(this.value))
def getUTCSeconds():
check_date(this)
if this.value is NaN:
return NaN
return SecFromTime(this.value)
def getMilliseconds():
check_date(this)
if this.value is NaN:
return NaN
return msFromTime(UTCToLocal(this.value))
def getUTCMilliseconds():
check_date(this)
if this.value is NaN:
return NaN
return msFromTime(this.value)
def getTimezoneOffset():
check_date(this)
if this.value is NaN:
return NaN
return (this.value - UTCToLocal(this.value)) // 60000
def setTime(time):
check_date(this)
this.value = TimeClip(time.to_number().to_int())
return this.value
def setMilliseconds(ms):
check_date(this)
t = UTCToLocal(this.value)
tim = MakeTime(
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
this.value = u
return u
def setUTCMilliseconds(ms):
check_date(this)
t = this.value
tim = MakeTime(
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
u = TimeClip(MakeDate(Day(t), tim))
this.value = u
return u
# todo Complete all setters!
def toUTCString():
check_date(this)
return this.utc_strftime('%d %B %Y %H:%M:%S')
def toISOString():
check_date(this)
t = this.value
year = YearFromTime(t)
month, day, hour, minute, second, milli = pad(
MonthFromTime(t) + 1), pad(DateFromTime(t)), pad(
HourFromTime(t)), pad(MinFromTime(t)), pad(
SecFromTime(t)), pad(msFromTime(t))
return ISO_FORMAT % (unicode(year) if 0 <= year <= 9999 else pad(
year, 6, True), month, day, hour, minute, second, milli)
def toJSON(key):
o = this.to_object()
tv = o.to_primitive('Number')
if tv.Class == 'Number' and not tv.is_finite():
return this.null
toISO = o.get('toISOString')
if not toISO.is_callable():
raise this.MakeError('TypeError', 'toISOString is not callable')
return toISO.call(o, ())
def pad(num, n=2, sign=False):
'''returns n digit string representation of the num'''
s = unicode(abs(num))
if len(s) < n:
s = '0' * (n - len(s)) + s
if not sign:
return s
if num >= 0:
return '+' + s
else:
return '-' + s
fill_prototype(DatePrototype, DateProto, default_attrs)
Date.define_own_property(
'prototype', {
'value': DatePrototype,
'enumerable': False,
'writable': False,
'configurable': False
})
DatePrototype.define_own_property('constructor', {
'value': Date,
'enumerable': False,
'writable': True,
'configurable': True
})
@@ -1,75 +0,0 @@
from ..base import *
from ..conversions import *
from ..func_utils import *
from pyjsparser import parse
from ..byte_trans import ByteCodeGenerator, Code
def Function(this, args):
# convert arguments to python list of strings
a = map(to_string, tuple(args))
_body = u';'
_args = ()
if len(a):
_body = u'%s;' % a[-1]
_args = a[:-1]
return executable_function(_body, _args, args.space, global_context=True)
def executable_function(_body, _args, space, global_context=True):
func_str = u'(function (%s) { ; %s ; });' % (u', '.join(_args), _body)
co = executable_code(
code_str=func_str, space=space, global_context=global_context)
return co()
# you can use this one lovely piece of function to compile and execute code on the fly! Watch out though as it may generate lots of code.
# todo tape cleanup? we dont know which pieces are needed and which are not so rather impossible without smarter machinery something like GC,
# a one solution would be to have a separate tape for functions
def executable_code(code_str, space, global_context=True):
# parse first to check if any SyntaxErrors
parsed = parse(code_str)
old_tape_len = len(space.byte_generator.exe.tape)
space.byte_generator.record_state()
start = space.byte_generator.exe.get_new_label()
skip = space.byte_generator.exe.get_new_label()
space.byte_generator.emit('JUMP', skip)
space.byte_generator.emit('LABEL', start)
space.byte_generator.emit(parsed)
space.byte_generator.emit('NOP')
space.byte_generator.emit('LABEL', skip)
space.byte_generator.emit('NOP')
space.byte_generator.restore_state()
space.byte_generator.exe.compile(
start_loc=old_tape_len
) # dont read the code from the beginning, dont be stupid!
ctx = space.GlobalObj if global_context else space.exe.current_ctx
def ex_code():
ret, status, token = space.byte_generator.exe.execute_fragment_under_context(
ctx, start, skip)
# todo Clean up the tape!
# this is NOT a way to do that because the fragment may contain the executable code! We dont want to remove it
#del space.byte_generator.exe.tape[old_tape_len:]
if status == 0:
return ret
elif status == 3:
raise token
else:
raise RuntimeError(
'Unexpected return status during JIT execution: %d' % status)
return ex_code
def _eval(this, args):
code_str = to_string(get_arg(args, 0))
return executable_code(code_str, args.space, global_context=True)()
def log(this, args):
print ' '.join(map(to_string, args))
return undefined
-157
View File
@@ -1,157 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
import math
import random
CONSTANTS = {
'E': 2.7182818284590452354,
'LN10': 2.302585092994046,
'LN2': 0.6931471805599453,
'LOG2E': 1.4426950408889634,
'LOG10E': 0.4342944819032518,
'PI': 3.1415926535897932,
'SQRT1_2': 0.7071067811865476,
'SQRT2': 1.4142135623730951
}
class MathFunctions:
def abs(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
return abs(a)
def acos(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
try:
return math.acos(a)
except:
return NaN
def asin(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
try:
return math.asin(a)
except:
return NaN
def atan(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
return math.atan(a)
def atan2(this, args):
x = get_arg(args, 0)
y = get_arg(args, 1)
a = to_number(x)
b = to_number(y)
if a != a or b != b: # it must be a nan
return NaN
return math.atan2(a, b)
def ceil(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
return float(math.ceil(a))
def floor(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
return float(math.floor(a))
def round(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
return float(round(a))
def sin(this, args):
x = get_arg(args, 0)
a = to_number(x)
if not is_finite(a): # it must be a nan
return NaN
return math.sin(a)
def cos(this, args):
x = get_arg(args, 0)
a = to_number(x)
if not is_finite(a): # it must be a nan
return NaN
return math.cos(a)
def tan(this, args):
x = get_arg(args, 0)
a = to_number(x)
if not is_finite(a): # it must be a nan
return NaN
return math.tan(a)
def log(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
try:
return math.log(a)
except:
return NaN
def exp(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
return math.exp(a)
def pow(this, args):
x = get_arg(args, 0)
y = get_arg(args, 1)
a = to_number(x)
b = to_number(y)
if a != a or b != b: # it must be a nan
return NaN
try:
return a**b
except:
return NaN
def sqrt(this, args):
x = get_arg(args, 0)
a = to_number(x)
if a != a: # it must be a nan
return NaN
try:
return a**0.5
except:
return NaN
def min(this, args):
if len(args) == 0:
return Infinity
return min(map(to_number, tuple(args)))
def max(this, args):
if len(args) == 0:
return -Infinity
return max(map(to_number, tuple(args)))
def random(this, args):
return random.random()
@@ -1,27 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
def Number(this, args):
if len(args) == 0:
return 0.
return to_number(args[0])
def NumberConstructor(args, space):
temp = space.NewObject()
temp.prototype = space.NumberPrototype
temp.Class = 'Number'
temp.value = float(to_number(get_arg(args, 0)) if len(args) > 0 else 0.)
return temp
CONSTS = {
'MAX_VALUE': 1.7976931348623157e308,
'MIN_VALUE': 5.0e-324,
'NaN': NaN,
'NEGATIVE_INFINITY': Infinity,
'POSITIVE_INFINITY': -Infinity
}
@@ -1,204 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
from ..base import is_data_descriptor
import six
def Object(this, args):
val = get_arg(args, 0)
if is_null(val) or is_undefined(val):
return args.space.NewObject()
return to_object(val, args.space)
def ObjectCreate(args, space):
if len(args):
val = get_arg(args, 0)
if is_object(val):
# Implementation dependent, but my will simply return :)
return val
elif type(val) in (NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE):
return to_object(val, space)
return space.NewObject()
class ObjectMethods:
def getPrototypeOf(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError('TypeError',
'Object.getPrototypeOf called on non-object')
return null if obj.prototype is None else obj.prototype
def getOwnPropertyDescriptor(this, args):
obj = get_arg(args, 0)
prop = get_arg(args, 1)
if not is_object(obj):
raise MakeError(
'TypeError',
'Object.getOwnPropertyDescriptor called on non-object')
desc = obj.own.get(to_string(prop))
return convert_to_js_type(desc, args.space)
def getOwnPropertyNames(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError(
'TypeError',
'Object.getOwnPropertyDescriptor called on non-object')
return args.space.ConstructArray(obj.own.keys())
def create(this, args):
obj = get_arg(args, 0)
if not (is_object(obj) or is_null(obj)):
raise MakeError('TypeError',
'Object prototype may only be an Object or null')
temp = args.space.NewObject()
temp.prototype = None if is_null(obj) else obj
if len(args) > 1 and not is_undefined(args[1]):
if six.PY2:
args.tup = (args[1], )
ObjectMethods.defineProperties.__func__(temp, args)
else:
args.tup = (args[1], )
ObjectMethods.defineProperties(temp, args)
return temp
def defineProperty(this, args):
obj = get_arg(args, 0)
prop = get_arg(args, 1)
attrs = get_arg(args, 2)
if not is_object(obj):
raise MakeError('TypeError',
'Object.defineProperty called on non-object')
name = to_string(prop)
if not obj.define_own_property(name, ToPropertyDescriptor(attrs),
False):
raise MakeError('TypeError', 'Cannot redefine property: %s' % name)
return obj
def defineProperties(this, args):
obj = get_arg(args, 0)
properties = get_arg(args, 1)
if not is_object(obj):
raise MakeError('TypeError',
'Object.defineProperties called on non-object')
props = to_object(properties, args.space)
for k, v in props.own.items():
if not v.get('enumerable'):
continue
desc = ToPropertyDescriptor(props.get(unicode(k)))
if not obj.define_own_property(unicode(k), desc, False):
raise MakeError('TypeError',
'Failed to define own property: %s' % k)
return obj
def seal(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError('TypeError', 'Object.seal called on non-object')
for desc in obj.own.values():
desc['configurable'] = False
obj.extensible = False
return obj
def freeze(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError('TypeError', 'Object.freeze called on non-object')
for desc in obj.own.values():
desc['configurable'] = False
if is_data_descriptor(desc):
desc['writable'] = False
obj.extensible = False
return obj
def preventExtensions(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError('TypeError',
'Object.preventExtensions on non-object')
obj.extensible = False
return obj
def isSealed(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError('TypeError',
'Object.isSealed called on non-object')
if obj.extensible:
return False
for desc in obj.own.values():
if desc.get('configurable'):
return False
return True
def isFrozen(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError('TypeError',
'Object.isFrozen called on non-object')
if obj.extensible:
return False
for desc in obj.own.values():
if desc.get('configurable'):
return False
if is_data_descriptor(desc) and desc.get('writable'):
return False
return True
def isExtensible(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError('TypeError',
'Object.isExtensible called on non-object')
return obj.extensible
def keys(this, args):
obj = get_arg(args, 0)
if not is_object(obj):
raise MakeError('TypeError', 'Object.keys called on non-object')
return args.space.ConstructArray([
unicode(e) for e, d in six.iteritems(obj.own)
if d.get('enumerable')
])
# some utility functions:
def ToPropertyDescriptor(obj): # page 38 (50 absolute)
if not is_object(obj):
raise MakeError('TypeError',
'Can\'t convert non-object to property descriptor')
desc = {}
if obj.has_property('enumerable'):
desc['enumerable'] = to_boolean(obj.get('enumerable'))
if obj.has_property('configurable'):
desc['configurable'] = to_boolean(obj.get('configurable'))
if obj.has_property('value'):
desc['value'] = obj.get('value')
if obj.has_property('writable'):
desc['writable'] = to_boolean(obj.get('writable'))
if obj.has_property('get'):
cand = obj.get('get')
if not (is_undefined(cand) or is_callable(cand)):
raise MakeError(
'TypeError',
'Invalid getter (it has to be a function or undefined)')
desc['get'] = cand
if obj.has_property('set'):
cand = obj.get('set')
if not (is_undefined(cand) or is_callable(cand)):
raise MakeError(
'TypeError',
'Invalid setter (it has to be a function or undefined)')
desc['set'] = cand
if ('get' in desc or 'set' in desc) and ('value' in desc
or 'writable' in desc):
raise MakeError(
'TypeError',
'Invalid property. A property cannot both have accessors and be writable or have a value.'
)
return desc
@@ -1,41 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
from ..base import SpaceTuple
REG_EXP_FLAGS = ('g', 'i', 'm')
def RegExp(this, args):
pattern = get_arg(args, 0)
flags = get_arg(args, 1)
if GetClass(pattern) == 'RegExp':
if not is_undefined(flags):
raise MakeError(
'TypeError',
'Cannot supply flags when constructing one RegExp from another'
)
# return unchanged
return pattern
#pattern is not a regexp
if is_undefined(pattern):
pattern = u''
else:
pattern = to_string(pattern)
flags = to_string(flags) if not is_undefined(flags) else u''
for flag in flags:
if flag not in REG_EXP_FLAGS:
raise MakeError(
'SyntaxError',
'Invalid flags supplied to RegExp constructor "%s"' % flag)
if len(set(flags)) != len(flags):
raise MakeError(
'SyntaxError',
'Invalid flags supplied to RegExp constructor "%s"' % flags)
return args.space.NewRegExp(pattern, flags)
def RegExpCreate(args, space):
_args = SpaceTuple(args)
_args.space = space
return RegExp(undefined, _args)
@@ -1,23 +0,0 @@
from ..conversions import *
from ..func_utils import *
def fromCharCode(this, args):
res = u''
for e in args:
res += unichr(to_uint16(e))
return res
def String(this, args):
if len(args) == 0:
return u''
return to_string(args[0])
def StringConstructor(args, space):
temp = space.NewObject()
temp.prototype = space.StringPrototype
temp.Class = 'String'
temp.value = to_string(get_arg(args, 0)) if len(args) > 0 else u''
return temp
@@ -1,209 +0,0 @@
from __future__ import unicode_literals
# NOTE: t must be INT!!!
import time
import datetime
import warnings
try:
from tzlocal import get_localzone
LOCAL_ZONE = get_localzone()
except: # except all problems...
warnings.warn(
'Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time'
)
class LOCAL_ZONE:
@staticmethod
def dst(*args):
return 1
from js2py.base import MakeError
CUM = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
msPerDay = 86400000
msPerYear = int(86400000 * 365.242)
msPerSecond = 1000
msPerMinute = 60000
msPerHour = 3600000
HoursPerDay = 24
MinutesPerHour = 60
SecondsPerMinute = 60
NaN = float('nan')
LocalTZA = -time.timezone * msPerSecond
def DaylightSavingTA(t):
if t is NaN:
return t
try:
return int(
LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(
t // 1000)).seconds) * 1000
except:
warnings.warn(
'Invalid datetime date, assumed DST time, may be inaccurate...',
Warning)
return 1
#raise MakeError('TypeError', 'date not supported by python.datetime. I will solve it in future versions')
def GetTimeZoneName(t):
return time.tzname[DaylightSavingTA(t) > 0]
def LocalToUTC(t):
return t - LocalTZA - DaylightSavingTA(t - LocalTZA)
def UTCToLocal(t):
return t + LocalTZA + DaylightSavingTA(t)
def Day(t):
return t // 86400000
def TimeWithinDay(t):
return t % 86400000
def DaysInYear(y):
if y % 4:
return 365
elif y % 100:
return 366
elif y % 400:
return 365
else:
return 366
def DayFromYear(y):
return 365 * (y - 1970) + (y - 1969) // 4 - (y - 1901) // 100 + (
y - 1601) // 400
def TimeFromYear(y):
return 86400000 * DayFromYear(y)
def YearFromTime(t):
guess = 1970 - t // 31556908800 # msPerYear
gt = TimeFromYear(guess)
if gt <= t:
while gt <= t:
guess += 1
gt = TimeFromYear(guess)
return guess - 1
else:
while gt > t:
guess -= 1
gt = TimeFromYear(guess)
return guess
def DayWithinYear(t):
return Day(t) - DayFromYear(YearFromTime(t))
def InLeapYear(t):
y = YearFromTime(t)
if y % 4:
return 0
elif y % 100:
return 1
elif y % 400:
return 0
else:
return 1
def MonthFromTime(t):
day = DayWithinYear(t)
leap = InLeapYear(t)
if day < 31:
return 0
day -= leap
if day < 59:
return 1
elif day < 90:
return 2
elif day < 120:
return 3
elif day < 151:
return 4
elif day < 181:
return 5
elif day < 212:
return 6
elif day < 243:
return 7
elif day < 273:
return 8
elif day < 304:
return 9
elif day < 334:
return 10
else:
return 11
def DateFromTime(t):
mon = MonthFromTime(t)
day = DayWithinYear(t)
return day - CUM[mon] - (1 if InLeapYear(t) and mon >= 2 else 0) + 1
def WeekDay(t):
# 0 == sunday
return (Day(t) + 4) % 7
def msFromTime(t):
return t % 1000
def SecFromTime(t):
return (t // 1000) % 60
def MinFromTime(t):
return (t // 60000) % 60
def HourFromTime(t):
return (t // 3600000) % 24
def MakeTime(hour, Min, sec, ms):
# takes PyJs objects and returns t
if not (hour.is_finite() and Min.is_finite() and sec.is_finite()
and ms.is_finite()):
return NaN
h, m, s, milli = hour.to_int(), Min.to_int(), sec.to_int(), ms.to_int()
return h * 3600000 + m * 60000 + s * 1000 + milli
def MakeDay(year, month, date):
# takes PyJs objects and returns t
if not (year.is_finite() and month.is_finite() and date.is_finite()):
return NaN
y, m, dt = year.to_int(), month.to_int(), date.to_int()
y += m // 12
mn = m % 12
d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y) == 366
and mn >= 2 else 0)
return d # ms per day
def MakeDate(day, time):
return 86400000 * day + time
def TimeClip(t):
if t != t or abs(t) == float('inf'):
return NaN
if abs(t) > 8.64 * 10**15:
return NaN
return int(t)
-141
View File
@@ -1,141 +0,0 @@
from __future__ import unicode_literals
# Type Conversions. to_type. All must return PyJs subclass instance
from .simplex import *
def to_primitive(self, hint=None):
if is_primitive(self):
return self
if hint is None and (self.Class == 'Number' or self.Class == 'Boolean'):
# favour number for Class== Number or Boolean default = String
hint = 'Number'
return self.default_value(hint)
def to_boolean(self):
typ = Type(self)
if typ == 'Boolean': # no need to convert
return self
elif typ == 'Null' or typ == 'Undefined': # they are both always false
return False
elif typ == 'Number': # false only for 0, and NaN
return self and self == self # test for nan (nan -> flase)
elif typ == 'String':
return bool(self)
else: # object - always True
return True
def to_number(self):
typ = Type(self)
if typ == 'Number': # or self.Class=='Number': # no need to convert
return self
elif typ == 'Null': # null is 0
return 0.
elif typ == 'Undefined': # undefined is NaN
return NaN
elif typ == 'Boolean': # 1 for True 0 for false
return float(self)
elif typ == 'String':
s = self.strip() # Strip white space
if not s: # '' is simply 0
return 0.
if 'x' in s or 'X' in s[:3]: # hex (positive only)
try: # try to convert
num = int(s, 16)
except ValueError: # could not convert -> NaN
return NaN
return float(num)
sign = 1 # get sign
if s[0] in '+-':
if s[0] == '-':
sign = -1
s = s[1:]
if s == 'Infinity': # Check for infinity keyword. 'NaN' will be NaN anyway.
return sign * Infinity
try: # decimal try
num = sign * float(s) # Converted
except ValueError:
return NaN # could not convert to decimal > return NaN
return float(num)
else: # object - most likely it will be NaN.
return to_number(to_primitive(self, 'Number'))
def to_string(self):
typ = Type(self)
if typ == 'String':
return self
elif typ == 'Null':
return 'null'
elif typ == 'Undefined':
return 'undefined'
elif typ == 'Boolean':
return 'true' if self else 'false'
elif typ == 'Number': # or self.Class=='Number':
return js_dtoa(self)
else: # object
return to_string(to_primitive(self, 'String'))
def to_object(self, space):
typ = Type(self)
if typ == 'Object':
return self
elif typ == 'Boolean': # Unsure ... todo check here
return space.Boolean.create((self, ), space)
elif typ == 'Number': # ?
return space.Number.create((self, ), space)
elif typ == 'String': # ?
return space.String.create((self, ), space)
elif typ == 'Null' or typ == 'Undefined':
raise MakeError('TypeError',
'undefined or null can\'t be converted to object')
else:
raise RuntimeError()
def to_int32(self):
num = to_number(self)
if is_nan(num) or is_infinity(num):
return 0
int32 = int(num) % 2**32
return int(int32 - 2**32 if int32 >= 2**31 else int32)
def to_int(self):
num = to_number(self)
if is_nan(num):
return 0
elif is_infinity(num):
return 10**20 if num > 0 else -10**20
return int(num)
def to_uint32(self):
num = to_number(self)
if is_nan(num) or is_infinity(num):
return 0
return int(num) % 2**32
def to_uint16(self):
num = to_number(self)
if is_nan(num) or is_infinity(num):
return 0
return int(num) % 2**16
def to_int16(self):
num = to_number(self)
if is_nan(num) or is_infinity(num):
return 0
int16 = int(num) % 2**16
return int(int16 - 2**16 if int16 >= 2**15 else int16)
def cok(self):
"""Check object coercible"""
if type(self) in (UNDEFINED_TYPE, NULL_TYPE):
raise MakeError('TypeError',
'undefined or null can\'t be converted to object')
-90
View File
@@ -1,90 +0,0 @@
# todo make sure what they mean by desc undefined? None or empty? Answer: None :) it can never be empty but None is sometimes returned.
# I am implementing everything as dicts to speed up property creation
# Warning: value, get, set props of dest are PyJs types. Rest is Py!
def is_data_descriptor(desc):
return desc and ('value' in desc or 'writable' in desc)
def is_accessor_descriptor(desc):
return desc and ('get' in desc or 'set' in desc)
def is_generic_descriptor(
desc
): # generic means not the data and not the setter - therefore it must be one that changes only enum and conf
return desc and not (is_data_descriptor(desc)
or is_accessor_descriptor(desc))
def from_property_descriptor(desc, space):
if not desc:
return {}
obj = space.NewObject()
if is_data_descriptor(desc):
obj.define_own_property(
'value', {
'value': desc['value'],
'writable': True,
'enumerable': True,
'configurable': True
}, False)
obj.define_own_property(
'writable', {
'value': desc['writable'],
'writable': True,
'enumerable': True,
'configurable': True
}, False)
else:
obj.define_own_property(
'get', {
'value': desc['get'],
'writable': True,
'enumerable': True,
'configurable': True
}, False)
obj.define_own_property(
'set', {
'value': desc['set'],
'writable': True,
'enumerable': True,
'configurable': True
}, False)
obj.define_own_property(
'writable', {
'value': desc['writable'],
'writable': True,
'enumerable': True,
'configurable': True
}, False)
obj.define_own_property(
'enumerable', {
'value': desc['enumerable'],
'writable': True,
'enumerable': True,
'configurable': True
}, False)
return obj
def to_property_descriptor(obj):
if obj._type() != 'Object':
raise TypeError()
desc = {}
for e in ('enumerable', 'configurable', 'writable'):
if obj.has_property(e):
desc[e] = obj.get(e).to_boolean().value
if obj.has_property('value'):
desc['value'] = obj.get('value')
for e in ('get', 'set'):
if obj.has_property(e):
cand = obj.get(e)
if not (cand.is_callable() or cand.is_undefined()):
raise TypeError()
if ('get' in desc or 'set' in desc) and ('value' in desc
or 'writable' in desc):
raise TypeError()
-281
View File
@@ -1,281 +0,0 @@
from __future__ import unicode_literals
from .base import Scope
from .func_utils import *
from .conversions import *
import six
from .prototypes.jsboolean import BooleanPrototype
from .prototypes.jserror import ErrorPrototype
from .prototypes.jsfunction import FunctionPrototype
from .prototypes.jsnumber import NumberPrototype
from .prototypes.jsobject import ObjectPrototype
from .prototypes.jsregexp import RegExpPrototype
from .prototypes.jsstring import StringPrototype
from .prototypes.jsarray import ArrayPrototype
from .prototypes import jsjson
from .prototypes import jsutils
from .constructors import jsnumber, jsstring, jsarray, jsboolean, jsregexp, jsmath, jsobject, jsfunction, jsconsole
def fill_proto(proto, proto_class, space):
for i in dir(proto_class):
e = getattr(proto_class, i)
if six.PY2:
if hasattr(e, '__func__'):
meth = e.__func__
else:
continue
else:
if hasattr(e, '__call__') and not i.startswith('__'):
meth = e
else:
continue
meth_name = meth.__name__.strip('_') # RexExp._exec -> RegExp.exec
js_meth = space.NewFunction(meth, space.ctx, (), meth_name, False, ())
set_non_enumerable(proto, meth_name, js_meth)
return proto
def easy_func(f, space):
return space.NewFunction(f, space.ctx, (), f.__name__, False, ())
def Empty(this, args):
return undefined
def set_non_enumerable(obj, name, prop):
obj.define_own_property(
unicode(name), {
'value': prop,
'writable': True,
'enumerable': False,
'configurable': True
}, True)
def set_protected(obj, name, prop):
obj.define_own_property(
unicode(name), {
'value': prop,
'writable': False,
'enumerable': False,
'configurable': False
}, True)
def fill_space(space, byte_generator):
# set global scope
global_scope = Scope({}, space, parent=None)
global_scope.THIS_BINDING = global_scope
global_scope.registers(byte_generator.declared_vars)
space.GlobalObj = global_scope
space.byte_generator = byte_generator
# first init all protos, later take care of constructors and details
# Function must be first obviously, we have to use a small trick to do that...
function_proto = space.NewFunction(Empty, space.ctx, (), 'Empty', False,
())
space.FunctionPrototype = function_proto # this will fill the prototypes of the methods!
fill_proto(function_proto, FunctionPrototype, space)
# Object next
object_proto = space.NewObject() # no proto
fill_proto(object_proto, ObjectPrototype, space)
space.ObjectPrototype = object_proto
function_proto.prototype = object_proto
# Number
number_proto = space.NewObject()
number_proto.prototype = object_proto
fill_proto(number_proto, NumberPrototype, space)
number_proto.value = 0.
number_proto.Class = 'Number'
space.NumberPrototype = number_proto
# String
string_proto = space.NewObject()
string_proto.prototype = object_proto
fill_proto(string_proto, StringPrototype, space)
string_proto.value = u''
string_proto.Class = 'String'
space.StringPrototype = string_proto
# Boolean
boolean_proto = space.NewObject()
boolean_proto.prototype = object_proto
fill_proto(boolean_proto, BooleanPrototype, space)
boolean_proto.value = False
boolean_proto.Class = 'Boolean'
space.BooleanPrototype = boolean_proto
# Array
array_proto = space.NewArray(0)
array_proto.prototype = object_proto
fill_proto(array_proto, ArrayPrototype, space)
space.ArrayPrototype = array_proto
# JSON
json = space.NewObject()
json.put(u'stringify', easy_func(jsjson.stringify, space))
json.put(u'parse', easy_func(jsjson.parse, space))
# Utils
parseFloat = easy_func(jsutils.parseFloat, space)
parseInt = easy_func(jsutils.parseInt, space)
isNaN = easy_func(jsutils.isNaN, space)
isFinite = easy_func(jsutils.isFinite, space)
# Error
error_proto = space.NewError(u'Error', u'')
error_proto.prototype = object_proto
error_proto.put(u'name', u'Error')
fill_proto(error_proto, ErrorPrototype, space)
space.ErrorPrototype = error_proto
def construct_constructor(typ):
def creator(this, args):
message = get_arg(args, 0)
if not is_undefined(message):
msg = to_string(message)
else:
msg = u''
return space.NewError(typ, msg)
j = easy_func(creator, space)
j.name = unicode(typ)
set_protected(j, 'prototype', space.ERROR_TYPES[typ])
set_non_enumerable(space.ERROR_TYPES[typ], 'constructor', j)
def new_create(args, space):
message = get_arg(args, 0)
if not is_undefined(message):
msg = to_string(message)
else:
msg = u''
return space.NewError(typ, msg)
j.create = new_create
return j
# fill remaining error types
error_constructors = {}
for err_type_name in (u'Error', u'EvalError', u'RangeError',
u'ReferenceError', u'SyntaxError', u'TypeError',
u'URIError'):
extra_err = space.NewError(u'Error', u'')
extra_err.put(u'name', err_type_name)
setattr(space, err_type_name + u'Prototype', extra_err)
error_constructors[err_type_name] = construct_constructor(
err_type_name)
assert space.TypeErrorPrototype is not None
# RegExp
regexp_proto = space.NewRegExp(u'(?:)', u'')
regexp_proto.prototype = object_proto
fill_proto(regexp_proto, RegExpPrototype, space)
space.RegExpPrototype = regexp_proto
# Json
# now all these boring constructors...
# Number
number = easy_func(jsnumber.Number, space)
space.Number = number
number.create = jsnumber.NumberConstructor
set_non_enumerable(number_proto, 'constructor', number)
set_protected(number, 'prototype', number_proto)
# number has some extra constants
for k, v in jsnumber.CONSTS.items():
set_protected(number, k, v)
# String
string = easy_func(jsstring.String, space)
space.String = string
string.create = jsstring.StringConstructor
set_non_enumerable(string_proto, 'constructor', string)
set_protected(string, 'prototype', string_proto)
# string has an extra function
set_non_enumerable(string, 'fromCharCode',
easy_func(jsstring.fromCharCode, space))
# Boolean
boolean = easy_func(jsboolean.Boolean, space)
space.Boolean = boolean
boolean.create = jsboolean.BooleanConstructor
set_non_enumerable(boolean_proto, 'constructor', boolean)
set_protected(boolean, 'prototype', boolean_proto)
# Array
array = easy_func(jsarray.Array, space)
space.Array = array
array.create = jsarray.ArrayConstructor
set_non_enumerable(array_proto, 'constructor', array)
set_protected(array, 'prototype', array_proto)
array.put(u'isArray', easy_func(jsarray.isArray, space))
# RegExp
regexp = easy_func(jsregexp.RegExp, space)
space.RegExp = regexp
regexp.create = jsregexp.RegExpCreate
set_non_enumerable(regexp_proto, 'constructor', regexp)
set_protected(regexp, 'prototype', regexp_proto)
# Object
_object = easy_func(jsobject.Object, space)
space.Object = _object
_object.create = jsobject.ObjectCreate
set_non_enumerable(object_proto, 'constructor', _object)
set_protected(_object, 'prototype', object_proto)
fill_proto(_object, jsobject.ObjectMethods, space)
# Function
function = easy_func(jsfunction.Function, space)
space.Function = function
# Math
math = space.NewObject()
math.Class = 'Math'
fill_proto(math, jsmath.MathFunctions, space)
for k, v in jsmath.CONSTANTS.items():
set_protected(math, k, v)
console = space.NewObject()
fill_proto(console, jsconsole.ConsoleMethods, space)
# set global object
builtins = {
'String': string,
'Number': number,
'Boolean': boolean,
'RegExp': regexp,
'exports': convert_to_js_type({}, space),
'Math': math,
#'Date',
'Object': _object,
'Function': function,
'JSON': json,
'Array': array,
'parseFloat': parseFloat,
'parseInt': parseInt,
'isFinite': isFinite,
'isNaN': isNaN,
'eval': easy_func(jsfunction._eval, space),
'console': console,
'log': console.get(u'log'),
}
builtins.update(error_constructors)
set_protected(global_scope, 'NaN', NaN)
set_protected(global_scope, 'Infinity', Infinity)
for k, v in builtins.items():
set_non_enumerable(global_scope, k, v)
-73
View File
@@ -1,73 +0,0 @@
from .simplex import *
from .conversions import *
import six
if six.PY3:
basestring = str
long = int
xrange = range
unicode = str
def get_arg(arguments, n):
if len(arguments) <= n:
return undefined
return arguments[n]
def ensure_js_types(args, space=None):
return tuple(convert_to_js_type(e, space=space) for e in args)
def convert_to_js_type(e, space=None):
t = type(e)
if is_js_type(e):
return e
if t in (int, long, float):
return float(e)
elif isinstance(t, basestring):
return unicode(t)
elif t in (list, tuple):
if space is None:
raise MakeError(
'TypeError',
'Actually an internal error, could not convert to js type because space not specified'
)
return space.ConstructArray(ensure_js_types(e, space=space))
elif t == dict:
if space is None:
raise MakeError(
'TypeError',
'Actually an internal error, could not convert to js type because space not specified'
)
new = {}
for k, v in e.items():
new[to_string(convert_to_js_type(k, space))] = convert_to_js_type(
v, space)
return space.ConstructObject(new)
else:
raise MakeError('TypeError', 'Could not convert to js type!')
def is_js_type(e):
if type(e) in PRIMITIVES:
return True
elif hasattr(e, 'Class') and hasattr(e, 'value'): # not perfect but works
return True
else:
return False
# todo optimise these 2!
def js_array_to_tuple(arr):
length = to_uint32(arr.get(u'length'))
return tuple(arr.get(unicode(e)) for e in xrange(length))
def js_array_to_list(arr):
length = to_uint32(arr.get(u'length'))
return [arr.get(unicode(e)) for e in xrange(length)]
def js_arr_length(arr):
return to_uint32(arr.get(u'length'))
View File
-805
View File
@@ -1,805 +0,0 @@
from .operations import *
from .base import get_member, get_member_dot, PyJsFunction, Scope
class OP_CODE(object):
_params = []
# def eval(self, ctx):
# raise
def __repr__(self):
return self.__class__.__name__ + str(
tuple([getattr(self, e) for e in self._params]))
# --------------------- UNARY ----------------------
class UNARY_OP(OP_CODE):
_params = ['operator']
def __init__(self, operator):
self.operator = operator
def eval(self, ctx):
val = ctx.stack.pop()
ctx.stack.append(UNARY_OPERATIONS[self.operator](val))
# special unary operations
class TYPEOF(OP_CODE):
_params = ['identifier']
def __init__(self, identifier):
self.identifier = identifier
def eval(self, ctx):
# typeof something_undefined does not throw reference error
val = ctx.get(self.identifier,
False) # <= this makes it slightly different!
ctx.stack.append(typeof_uop(val))
class POSTFIX(OP_CODE):
_params = ['cb', 'ca', 'identifier']
def __init__(self, post, incr, identifier):
self.identifier = identifier
self.cb = 1 if incr else -1
self.ca = -self.cb if post else 0
def eval(self, ctx):
target = to_number(ctx.get(self.identifier)) + self.cb
ctx.put(self.identifier, target)
ctx.stack.append(target + self.ca)
class POSTFIX_MEMBER(OP_CODE):
_params = ['cb', 'ca']
def __init__(self, post, incr):
self.cb = 1 if incr else -1
self.ca = -self.cb if post else 0
def eval(self, ctx):
name = ctx.stack.pop()
left = ctx.stack.pop()
target = to_number(get_member(left, name, ctx.space)) + self.cb
if type(left) not in PRIMITIVES:
left.put_member(name, target)
ctx.stack.append(target + self.ca)
class POSTFIX_MEMBER_DOT(OP_CODE):
_params = ['cb', 'ca', 'prop']
def __init__(self, post, incr, prop):
self.cb = 1 if incr else -1
self.ca = -self.cb if post else 0
self.prop = prop
def eval(self, ctx):
left = ctx.stack.pop()
target = to_number(get_member_dot(left, self.prop,
ctx.space)) + self.cb
if type(left) not in PRIMITIVES:
left.put(self.prop, target)
ctx.stack.append(target + self.ca)
class DELETE(OP_CODE):
_params = ['name']
def __init__(self, name):
self.name = name
def eval(self, ctx):
ctx.stack.append(ctx.delete(self.name))
class DELETE_MEMBER(OP_CODE):
def eval(self, ctx):
prop = to_string(ctx.stack.pop())
obj = to_object(ctx.stack.pop(), ctx)
ctx.stack.append(obj.delete(prop, False))
# --------------------- BITWISE ----------------------
class BINARY_OP(OP_CODE):
_params = ['operator']
def __init__(self, operator):
self.operator = operator
def eval(self, ctx):
right = ctx.stack.pop()
left = ctx.stack.pop()
ctx.stack.append(BINARY_OPERATIONS[self.operator](left, right))
# &&, || and conditional are implemented in bytecode
# --------------------- JUMPS ----------------------
# simple label that will be removed from code after compilation. labels ID will be translated
# to source code position.
class LABEL(OP_CODE):
_params = ['num']
def __init__(self, num):
self.num = num
# I implemented interpreter in the way that when an integer is returned by eval operation the execution will jump
# to the location of the label (it is loc = label_locations[label])
class BASE_JUMP(OP_CODE):
_params = ['label']
def __init__(self, label):
self.label = label
class JUMP(BASE_JUMP):
def eval(self, ctx):
return self.label
class JUMP_IF_TRUE(BASE_JUMP):
def eval(self, ctx):
val = ctx.stack.pop()
if to_boolean(val):
return self.label
class JUMP_IF_EQ(BASE_JUMP):
# this one is used in switch statement - compares last 2 values using === operator and jumps popping both if true else pops last.
def eval(self, ctx):
cmp = ctx.stack.pop()
if strict_equality_op(ctx.stack[-1], cmp):
ctx.stack.pop()
return self.label
class JUMP_IF_TRUE_WITHOUT_POP(BASE_JUMP):
def eval(self, ctx):
val = ctx.stack[-1]
if to_boolean(val):
return self.label
class JUMP_IF_FALSE(BASE_JUMP):
def eval(self, ctx):
val = ctx.stack.pop()
if not to_boolean(val):
return self.label
class JUMP_IF_FALSE_WITHOUT_POP(BASE_JUMP):
def eval(self, ctx):
val = ctx.stack[-1]
if not to_boolean(val):
return self.label
class POP(OP_CODE):
def eval(self, ctx):
# todo remove this check later
assert len(ctx.stack), 'Popped from empty stack!'
del ctx.stack[-1]
# class REDUCE(OP_CODE):
# def eval(self, ctx):
# assert len(ctx.stack)==2
# ctx.stack[0] = ctx.stack[1]
# del ctx.stack[1]
# --------------- LOADING --------------
class LOAD_NONE(OP_CODE): # be careful with this :)
_params = []
def eval(self, ctx):
ctx.stack.append(None)
class LOAD_N_TUPLE(
OP_CODE
): # loads the tuple composed of n last elements on stack. elements are popped.
_params = ['n']
def __init__(self, n):
self.n = n
def eval(self, ctx):
tup = tuple(ctx.stack[-self.n:])
del ctx.stack[-self.n:]
ctx.stack.append(tup)
class LOAD_UNDEFINED(OP_CODE):
def eval(self, ctx):
ctx.stack.append(undefined)
class LOAD_NULL(OP_CODE):
def eval(self, ctx):
ctx.stack.append(null)
class LOAD_BOOLEAN(OP_CODE):
_params = ['val']
def __init__(self, val):
assert val in (0, 1)
self.val = bool(val)
def eval(self, ctx):
ctx.stack.append(self.val)
class LOAD_STRING(OP_CODE):
_params = ['val']
def __init__(self, val):
assert isinstance(val, basestring)
self.val = unicode(val)
def eval(self, ctx):
ctx.stack.append(self.val)
class LOAD_NUMBER(OP_CODE):
_params = ['val']
def __init__(self, val):
assert isinstance(val, (float, int, long))
self.val = float(val)
def eval(self, ctx):
ctx.stack.append(self.val)
class LOAD_REGEXP(OP_CODE):
_params = ['body', 'flags']
def __init__(self, body, flags):
self.body = body
self.flags = flags
def eval(self, ctx):
# we have to generate a new regexp - they are mutable
ctx.stack.append(ctx.space.NewRegExp(self.body, self.flags))
class LOAD_FUNCTION(OP_CODE):
_params = ['start', 'params', 'name', 'is_declaration', 'definitions']
def __init__(self, start, params, name, is_declaration, definitions):
assert type(start) == int
self.start = start # its an ID of label pointing to the beginning of the function bytecode
self.params = params
self.name = name
self.is_declaration = bool(is_declaration)
self.definitions = tuple(set(definitions + params))
def eval(self, ctx):
ctx.stack.append(
ctx.space.NewFunction(self.start, ctx, self.params, self.name,
self.is_declaration, self.definitions))
class LOAD_OBJECT(OP_CODE):
_params = [
'props'
] # props are py string pairs (prop_name, kind): kind can be either i, g or s. (init, get, set)
def __init__(self, props):
self.num = len(props)
self.props = props
def eval(self, ctx):
obj = ctx.space.NewObject()
if self.num:
obj._init(self.props, ctx.stack[-self.num:])
del ctx.stack[-self.num:]
ctx.stack.append(obj)
class LOAD_ARRAY(OP_CODE):
_params = ['num']
def __init__(self, num):
self.num = num
def eval(self, ctx):
arr = ctx.space.NewArray(self.num)
if self.num:
arr._init(ctx.stack[-self.num:])
del ctx.stack[-self.num:]
ctx.stack.append(arr)
class LOAD_THIS(OP_CODE):
def eval(self, ctx):
ctx.stack.append(ctx.THIS_BINDING)
class LOAD(OP_CODE): # todo check!
_params = ['identifier']
def __init__(self, identifier):
self.identifier = identifier
# 11.1.2
def eval(self, ctx):
ctx.stack.append(ctx.get(self.identifier, throw=True))
class LOAD_MEMBER(OP_CODE):
def eval(self, ctx):
prop = ctx.stack.pop()
obj = ctx.stack.pop()
ctx.stack.append(get_member(obj, prop, ctx.space))
class LOAD_MEMBER_DOT(OP_CODE):
_params = ['prop']
def __init__(self, prop):
self.prop = prop
def eval(self, ctx):
obj = ctx.stack.pop()
ctx.stack.append(get_member_dot(obj, self.prop, ctx.space))
# --------------- STORING --------------
class STORE(OP_CODE):
_params = ['identifier']
def __init__(self, identifier):
self.identifier = identifier
def eval(self, ctx):
value = ctx.stack[-1] # don't pop
ctx.put(self.identifier, value)
class STORE_MEMBER(OP_CODE):
def eval(self, ctx):
value = ctx.stack.pop()
name = ctx.stack.pop()
left = ctx.stack.pop()
typ = type(left)
if typ in PRIMITIVES:
prop = to_string(name)
if typ == NULL_TYPE:
raise MakeError('TypeError',
"Cannot set property '%s' of null" % prop)
elif typ == UNDEFINED_TYPE:
raise MakeError('TypeError',
"Cannot set property '%s' of undefined" % prop)
# just ignore...
else:
left.put_member(name, value)
ctx.stack.append(value)
class STORE_MEMBER_DOT(OP_CODE):
_params = ['prop']
def __init__(self, prop):
self.prop = prop
def eval(self, ctx):
value = ctx.stack.pop()
left = ctx.stack.pop()
typ = type(left)
if typ in PRIMITIVES:
if typ == NULL_TYPE:
raise MakeError('TypeError',
"Cannot set property '%s' of null" % self.prop)
elif typ == UNDEFINED_TYPE:
raise MakeError(
'TypeError',
"Cannot set property '%s' of undefined" % self.prop)
# just ignore...
else:
left.put(self.prop, value)
ctx.stack.append(value)
class STORE_OP(OP_CODE):
_params = ['identifier', 'op']
def __init__(self, identifier, op):
self.identifier = identifier
self.op = op
def eval(self, ctx):
value = ctx.stack.pop()
new_value = BINARY_OPERATIONS[self.op](ctx.get(self.identifier), value)
ctx.put(self.identifier, new_value)
ctx.stack.append(new_value)
class STORE_MEMBER_OP(OP_CODE):
_params = ['op']
def __init__(self, op):
self.op = op
def eval(self, ctx):
value = ctx.stack.pop()
name = ctx.stack.pop()
left = ctx.stack.pop()
typ = type(left)
if typ in PRIMITIVES:
if typ is NULL_TYPE:
raise MakeError(
'TypeError',
"Cannot set property '%s' of null" % to_string(name))
elif typ is UNDEFINED_TYPE:
raise MakeError(
'TypeError',
"Cannot set property '%s' of undefined" % to_string(name))
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(
left, name, ctx.space), value))
return
else:
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(
left, name, ctx.space), value))
left.put_member(name, ctx.stack[-1])
class STORE_MEMBER_DOT_OP(OP_CODE):
_params = ['prop', 'op']
def __init__(self, prop, op):
self.prop = prop
self.op = op
def eval(self, ctx):
value = ctx.stack.pop()
left = ctx.stack.pop()
typ = type(left)
if typ in PRIMITIVES:
if typ == NULL_TYPE:
raise MakeError('TypeError',
"Cannot set property '%s' of null" % self.prop)
elif typ == UNDEFINED_TYPE:
raise MakeError(
'TypeError',
"Cannot set property '%s' of undefined" % self.prop)
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(
left, self.prop, ctx.space), value))
return
else:
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(
left, self.prop, ctx.space), value))
left.put(self.prop, ctx.stack[-1])
# --------------- CALLS --------------
def bytecode_call(ctx, func, this, args):
if type(func) is not PyJsFunction:
raise MakeError('TypeError', "%s is not a function" % Type(func))
if func.is_native: # call to built-in function or method
ctx.stack.append(func.call(this, args))
return None
# therefore not native. we have to return (new_context, function_label) to instruct interpreter to call
return func._generate_my_context(this, args), func.code
class CALL(OP_CODE):
def eval(self, ctx):
args = ctx.stack.pop()
func = ctx.stack.pop()
return bytecode_call(ctx, func, ctx.space.GlobalObj, args)
class CALL_METHOD(OP_CODE):
def eval(self, ctx):
args = ctx.stack.pop()
prop = ctx.stack.pop()
base = ctx.stack.pop()
func = get_member(base, prop, ctx.space)
return bytecode_call(ctx, func, base, args)
class CALL_METHOD_DOT(OP_CODE):
_params = ['prop']
def __init__(self, prop):
self.prop = prop
def eval(self, ctx):
args = ctx.stack.pop()
base = ctx.stack.pop()
func = get_member_dot(base, self.prop, ctx.space)
return bytecode_call(ctx, func, base, args)
class CALL_NO_ARGS(OP_CODE):
def eval(self, ctx):
func = ctx.stack.pop()
return bytecode_call(ctx, func, ctx.space.GlobalObj, ())
class CALL_METHOD_NO_ARGS(OP_CODE):
def eval(self, ctx):
prop = ctx.stack.pop()
base = ctx.stack.pop()
func = get_member(base, prop, ctx.space)
return bytecode_call(ctx, func, base, ())
class CALL_METHOD_DOT_NO_ARGS(OP_CODE):
_params = ['prop']
def __init__(self, prop):
self.prop = prop
def eval(self, ctx):
base = ctx.stack.pop()
func = get_member_dot(base, self.prop, ctx.space)
return bytecode_call(ctx, func, base, ())
class NOP(OP_CODE):
def eval(self, ctx):
pass
class RETURN(OP_CODE):
def eval(
self, ctx
): # remember to load the return value on stack before using RETURN op.
return (None, None)
class NEW(OP_CODE):
def eval(self, ctx):
args = ctx.stack.pop()
constructor = ctx.stack.pop()
if type(constructor) in PRIMITIVES or not hasattr(
constructor, 'create'):
raise MakeError('TypeError',
'%s is not a constructor' % Type(constructor))
ctx.stack.append(constructor.create(args, space=ctx.space))
class NEW_NO_ARGS(OP_CODE):
def eval(self, ctx):
constructor = ctx.stack.pop()
if type(constructor) in PRIMITIVES or not hasattr(
constructor, 'create'):
raise MakeError('TypeError',
'%s is not a constructor' % Type(constructor))
ctx.stack.append(constructor.create((), space=ctx.space))
# --------------- EXCEPTIONS --------------
class THROW(OP_CODE):
def eval(self, ctx):
raise MakeError(None, None, ctx.stack.pop())
class TRY_CATCH_FINALLY(OP_CODE):
_params = [
'try_label', 'catch_label', 'catch_variable', 'finally_label',
'finally_present', 'end_label'
]
def __init__(self, try_label, catch_label, catch_variable, finally_label,
finally_present, end_label):
self.try_label = try_label
self.catch_label = catch_label
self.catch_variable = catch_variable
self.finally_label = finally_label
self.finally_present = finally_present
self.end_label = end_label
def eval(self, ctx):
# 4 different exectution results
# 0=normal, 1=return, 2=jump_outside, 3=errors
# execute_fragment_under_context returns:
# (return_value, typ, jump_loc/error)
ctx.stack.pop()
# execute try statement
try_status = ctx.space.exe.execute_fragment_under_context(
ctx, self.try_label, self.catch_label)
errors = try_status[1] == 3
# catch
if errors and self.catch_variable is not None:
# generate catch block context...
catch_context = Scope({
self.catch_variable:
try_status[2].get_thrown_value(ctx.space)
}, ctx.space, ctx)
catch_context.THIS_BINDING = ctx.THIS_BINDING
catch_status = ctx.space.exe.execute_fragment_under_context(
catch_context, self.catch_label, self.finally_label)
else:
catch_status = None
# finally
if self.finally_present:
finally_status = ctx.space.exe.execute_fragment_under_context(
ctx, self.finally_label, self.end_label)
else:
finally_status = None
# now return controls
other_status = catch_status or try_status
if finally_status is None or (finally_status[1] == 0
and other_status[1] != 0):
winning_status = other_status
else:
winning_status = finally_status
val, typ, spec = winning_status
if typ == 0: # normal
ctx.stack.append(val)
return
elif typ == 1: # return
ctx.stack.append(spec)
return None, None # send return signal
elif typ == 2: # jump outside
ctx.stack.append(val)
return spec
elif typ == 3:
# throw is made with empty stack as usual
raise spec
else:
raise RuntimeError('Invalid return code')
# ------------ WITH + ITERATORS ----------
class WITH(OP_CODE):
_params = ['beg_label', 'end_label']
def __init__(self, beg_label, end_label):
self.beg_label = beg_label
self.end_label = end_label
def eval(self, ctx):
obj = to_object(ctx.stack.pop(), ctx.space)
with_context = Scope(
obj, ctx.space, ctx) # todo actually use the obj to modify the ctx
with_context.THIS_BINDING = ctx.THIS_BINDING
status = ctx.space.exe.execute_fragment_under_context(
with_context, self.beg_label, self.end_label)
val, typ, spec = status
if typ != 3: # exception
ctx.stack.pop()
if typ == 0: # normal
ctx.stack.append(val)
return
elif typ == 1: # return
ctx.stack.append(spec)
return None, None # send return signal
elif typ == 2: # jump outside
ctx.stack.append(val)
return spec
elif typ == 3: # exception
# throw is made with empty stack as usual
raise spec
else:
raise RuntimeError('Invalid return code')
class FOR_IN(OP_CODE):
_params = ['name', 'body_start_label', 'continue_label', 'break_label']
def __init__(self, name, body_start_label, continue_label, break_label):
self.name = name
self.body_start_label = body_start_label
self.continue_label = continue_label
self.break_label = break_label
def eval(self, ctx):
iterable = ctx.stack.pop()
if is_null(iterable) or is_undefined(iterable):
ctx.stack.pop()
ctx.stack.append(undefined)
return self.break_label
obj = to_object(iterable, ctx.space)
for e in sorted(obj.own):
if not obj.own[e]['enumerable']:
continue
ctx.put(
self.name, e
) # JS would have been so much nicer if this was ctx.space.put(self.name, obj.get(e))
# evaluate the body
status = ctx.space.exe.execute_fragment_under_context(
ctx, self.body_start_label, self.break_label)
val, typ, spec = status
if typ != 3: # exception
ctx.stack.pop()
if typ == 0: # normal
ctx.stack.append(val)
continue
elif typ == 1: # return
ctx.stack.append(spec)
return None, None # send return signal
elif typ == 2: # jump outside
# now have to figure out whether this is a continue or something else...
ctx.stack.append(val)
if spec == self.continue_label:
# just a continue, perform next iteration as normal
continue
return spec # break or smth, go there and finish the iteration
elif typ == 3: # exception
# throw is made with empty stack as usual
raise spec
else:
raise RuntimeError('Invalid return code')
return self.break_label
# all opcodes...
OP_CODES = {}
g = ''
for g in globals():
try:
if not issubclass(globals()[g], OP_CODE) or g is 'OP_CODE':
continue
except:
continue
OP_CODES[g] = globals()[g]
-314
View File
@@ -1,314 +0,0 @@
from __future__ import unicode_literals
from .simplex import *
from .conversions import *
# ------------------------------------------------------------------------------
# Unary operations
# -x
def minus_uop(self):
return -to_number(self)
# +x
def plus_uop(self): # +u
return to_number(self)
# !x
def logical_negation_uop(self): # !u cant do 'not u' :(
return not to_boolean(self)
# typeof x
def typeof_uop(self):
if is_callable(self):
return u'function'
typ = Type(self).lower()
if typ == u'null':
typ = u'object' # absolutely idiotic...
return typ
# ~u
def bit_invert_uop(self):
return float(to_int32(float(~to_int32(self))))
# void
def void_op(self):
return undefined
UNARY_OPERATIONS = {
'+': plus_uop,
'-': minus_uop,
'!': logical_negation_uop,
'~': bit_invert_uop,
'void': void_op,
'typeof':
typeof_uop, # this one only for member expressions! for identifiers its slightly different...
}
# ------------------------------------------------------------------------------
# ----- binary ops -------
# Bitwise operators
# <<, >>, &, ^, |, ~
# <<
def bit_lshift_op(self, other):
lnum = to_int32(self)
rnum = to_uint32(other)
shiftCount = rnum & 0x1F
return float(to_int32(float(lnum << shiftCount)))
# >>
def bit_rshift_op(self, other):
lnum = to_int32(self)
rnum = to_uint32(other)
shiftCount = rnum & 0x1F
return float(to_int32(float(lnum >> shiftCount)))
# >>>
def bit_bshift_op(self, other):
lnum = to_uint32(self)
rnum = to_uint32(other)
shiftCount = rnum & 0x1F
return float(to_uint32(float(lnum >> shiftCount)))
# &
def bit_and_op(self, other):
lnum = to_int32(self)
rnum = to_int32(other)
return float(to_int32(float(lnum & rnum)))
# ^
def bit_xor_op(self, other):
lnum = to_int32(self)
rnum = to_int32(other)
return float(to_int32(float(lnum ^ rnum)))
# |
def bit_or_op(self, other):
lnum = to_int32(self)
rnum = to_int32(other)
return float(to_int32(float(lnum | rnum)))
# Additive operators
# + and - are implemented here
# +
def add_op(self, other):
if type(self) is float and type(other) is float:
return self + other
if type(self) is unicode and type(other) is unicode:
return self + other
# standard way...
a = to_primitive(self)
b = to_primitive(other)
if type(a) is unicode or type(b) is unicode: # string wins hehe
return to_string(a) + to_string(b)
return to_number(a) + to_number(b)
# -
def sub_op(self, other):
return to_number(self) - to_number(other)
# Multiplicative operators
# *, / and % are implemented here
# *
def mul_op(self, other):
return to_number(self) * to_number(other)
# /
def div_op(self, other):
a = to_number(self)
b = to_number(other)
if b:
return a / float(b) # ensure at least one is a float.
if not a or a != a:
return NaN
return Infinity if a > 0 else -Infinity
# %
def mod_op(self, other):
a = to_number(self)
b = to_number(other)
if abs(a) == Infinity or not b:
return NaN
if abs(b) == Infinity:
return a
pyres = a % b # different signs in python and javascript
# python has the same sign as b and js has the same
# sign as a.
if a < 0 and pyres > 0:
pyres -= abs(b)
elif a > 0 and pyres < 0:
pyres += abs(b)
return float(pyres)
# Comparisons
# <, <=, !=, ==, >=, > are implemented here.
def abstract_relational_comparison(self, other,
self_first=True): # todo speed up!
''' self<other if self_first else other<self.
Returns the result of the question: is self smaller than other?
in case self_first is false it returns the answer of:
is other smaller than self.
result is PyJs type: bool or undefined'''
px = to_primitive(self, 'Number')
py = to_primitive(other, 'Number')
if not self_first: # reverse order
px, py = py, px
if not (Type(px) == 'String' and Type(py) == 'String'):
px, py = to_number(px), to_number(py)
if is_nan(px) or is_nan(py):
return None # watch out here!
return px < py # same cmp algorithm
else:
# I am pretty sure that python has the same
# string cmp algorithm but I have to confirm it
return px < py
# <
def less_op(self, other):
res = abstract_relational_comparison(self, other, True)
if res is None:
return False
return res
# <=
def less_eq_op(self, other):
res = abstract_relational_comparison(self, other, False)
if res is None:
return False
return not res
# >=
def greater_eq_op(self, other):
res = abstract_relational_comparison(self, other, True)
if res is None:
return False
return not res
# >
def greater_op(self, other):
res = abstract_relational_comparison(self, other, False)
if res is None:
return False
return res
# equality
def abstract_equality_op(self, other):
''' returns the result of JS == compare.
result is PyJs type: bool'''
tx, ty = Type(self), Type(other)
if tx == ty:
if tx == 'Undefined' or tx == 'Null':
return True
if tx == 'Number' or tx == 'String' or tx == 'Boolean':
return self == other
return self is other # Object
elif (tx == 'Undefined' and ty == 'Null') or (ty == 'Undefined'
and tx == 'Null'):
return True
elif tx == 'Number' and ty == 'String':
return abstract_equality_op(self, to_number(other))
elif tx == 'String' and ty == 'Number':
return abstract_equality_op(to_number(self), other)
elif tx == 'Boolean':
return abstract_equality_op(to_number(self), other)
elif ty == 'Boolean':
return abstract_equality_op(self, to_number(other))
elif (tx == 'String' or tx == 'Number') and is_object(other):
return abstract_equality_op(self, to_primitive(other))
elif (ty == 'String' or ty == 'Number') and is_object(self):
return abstract_equality_op(to_primitive(self), other)
else:
return False
def abstract_inequality_op(self, other):
return not abstract_equality_op(self, other)
def strict_equality_op(self, other):
typ = Type(self)
if typ != Type(other):
return False
if typ == 'Undefined' or typ == 'Null':
return True
if typ == 'Boolean' or typ == 'String' or typ == 'Number':
return self == other
else: # object
return self is other # Id compare.
def strict_inequality_op(self, other):
return not strict_equality_op(self, other)
def instanceof_op(self, other):
'''checks if self is instance of other'''
if not hasattr(other, 'has_instance'):
return False
return other.has_instance(self)
def in_op(self, other):
'''checks if self is in other'''
if not is_object(other):
raise MakeError(
'TypeError',
"You can\'t use 'in' operator to search in non-objects")
return other.has_property(to_string(self))
BINARY_OPERATIONS = {
'+': add_op,
'-': sub_op,
'*': mul_op,
'/': div_op,
'%': mod_op,
'<<': bit_lshift_op,
'>>': bit_rshift_op,
'>>>': bit_bshift_op,
'|': bit_or_op,
'&': bit_and_op,
'^': bit_xor_op,
'==': abstract_equality_op,
'!=': abstract_inequality_op,
'===': strict_equality_op,
'!==': strict_inequality_op,
'<': less_op,
'<=': less_eq_op,
'>': greater_op,
'>=': greater_eq_op,
'in': in_op,
'instanceof': instanceof_op,
}
@@ -1 +0,0 @@
__author__ = 'Piotr Dabkowski'
-489
View File
@@ -1,489 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
from ..operations import strict_equality_op
import six
if six.PY3:
xrange = range
import functools
ARR_STACK = set({})
class ArrayPrototype:
def toString(this, args):
arr = to_object(this, args.space)
func = arr.get('join')
if not is_callable(func):
return u'[object %s]' % GetClass(arr)
return func.call(this, ())
def toLocaleString(this, args):
array = to_object(this, args.space)
arr_len = js_arr_length(array)
# separator is simply a comma ','
if not arr_len:
return ''
res = []
for i in xrange(arr_len):
element = array.get(unicode(i))
if is_undefined(element) or is_null(element):
res.append('')
else:
cand = to_object(element, args.space)
str_func = cand.get('toLocaleString')
if not is_callable(str_func):
raise MakeError(
'TypeError',
'toLocaleString method of item at index %d is not callable'
% i)
res.append(to_string(str_func.call(cand, ())))
return ','.join(res)
def concat(this, args):
array = to_object(this, args.space)
items = [array]
items.extend(tuple(args))
A = []
for E in items:
if GetClass(E) == 'Array':
k = 0
e_len = js_arr_length(E)
while k < e_len:
if E.has_property(unicode(k)):
A.append(E.get(unicode(k)))
k += 1
else:
A.append(E)
return args.space.ConstructArray(A)
def join(this, args):
ARR_STACK.add(this)
array = to_object(this, args.space)
separator = get_arg(args, 0)
arr_len = js_arr_length(array)
separator = ',' if is_undefined(separator) else to_string(separator)
elems = []
for e in xrange(arr_len):
elem = array.get(unicode(e))
if elem in ARR_STACK:
s = ''
else:
s = to_string(elem)
elems.append(
s if not (is_undefined(elem) or is_null(elem)) else '')
res = separator.join(elems)
ARR_STACK.remove(this)
return res
def pop(this, args): #todo check
array = to_object(this, args.space)
arr_len = js_arr_length(array)
if not arr_len:
array.put('length', float(arr_len))
return undefined
ind = unicode(arr_len - 1)
element = array.get(ind)
array.delete(ind)
array.put('length', float(arr_len - 1))
return element
def push(this, args):
array = to_object(this, args.space)
arr_len = js_arr_length(array)
to_put = tuple(args)
i = arr_len
for i, e in enumerate(to_put, arr_len):
array.put(unicode(i), e, True)
array.put('length', float(arr_len + len(to_put)), True)
return float(i)
def reverse(this, args):
array = to_object(this, args.space)
vals = js_array_to_list(array)
has_props = [
array.has_property(unicode(e))
for e in xrange(js_arr_length(array))
]
vals.reverse()
has_props.reverse()
for i, val in enumerate(vals):
if has_props[i]:
array.put(unicode(i), val)
else:
array.delete(unicode(i))
return array
def shift(this, args):
array = to_object(this, args.space)
arr_len = js_arr_length(array)
if not arr_len:
array.put('length', 0.)
return undefined
first = array.get('0')
for k in xrange(1, arr_len):
from_s, to_s = unicode(k), unicode(k - 1)
if array.has_property(from_s):
array.put(to_s, array.get(from_s))
else:
array.delete(to_s)
array.delete(unicode(arr_len - 1))
array.put('length', float(arr_len - 1))
return first
def slice(this, args): # todo check
array = to_object(this, args.space)
start = get_arg(args, 0)
end = get_arg(args, 1)
arr_len = js_arr_length(array)
relative_start = to_int(start)
k = max((arr_len + relative_start), 0) if relative_start < 0 else min(
relative_start, arr_len)
relative_end = arr_len if is_undefined(end) else to_int(end)
final = max((arr_len + relative_end), 0) if relative_end < 0 else min(
relative_end, arr_len)
res = []
n = 0
while k < final:
pk = unicode(k)
if array.has_property(pk):
res.append(array.get(pk))
k += 1
n += 1
return args.space.ConstructArray(res)
def sort(
this, args
): # todo: this assumes array continous (not sparse) - fix for sparse arrays
cmpfn = get_arg(args, 0)
if not GetClass(this) in ('Array', 'Arguments'):
return to_object(this, args.space) # do nothing
arr_len = js_arr_length(this)
if not arr_len:
return this
arr = [
(this.get(unicode(e)) if this.has_property(unicode(e)) else None)
for e in xrange(arr_len)
]
if not is_callable(cmpfn):
cmpfn = None
cmp = lambda a, b: sort_compare(a, b, cmpfn)
if six.PY3:
key = functools.cmp_to_key(cmp)
arr.sort(key=key)
else:
arr.sort(cmp=cmp)
for i in xrange(arr_len):
if arr[i] is None:
this.delete(unicode(i))
else:
this.put(unicode(i), arr[i])
return this
def splice(this, args):
# 1-8
array = to_object(this, args.space)
start = get_arg(args, 0)
deleteCount = get_arg(args, 1)
arr_len = js_arr_length(this)
relative_start = to_int(start)
actual_start = max(
(arr_len + relative_start), 0) if relative_start < 0 else min(
relative_start, arr_len)
actual_delete_count = min(
max(to_int(deleteCount), 0), arr_len - actual_start)
k = 0
A = args.space.NewArray(0)
# 9
while k < actual_delete_count:
if array.has_property(unicode(actual_start + k)):
A.put(unicode(k), array.get(unicode(actual_start + k)))
k += 1
# 10-11
items = list(args)[2:]
items_len = len(items)
# 12
if items_len < actual_delete_count:
k = actual_start
while k < (arr_len - actual_delete_count):
fr = unicode(k + actual_delete_count)
to = unicode(k + items_len)
if array.has_property(fr):
array.put(to, array.get(fr))
else:
array.delete(to)
k += 1
k = arr_len
while k > (arr_len - actual_delete_count + items_len):
array.delete(unicode(k - 1))
k -= 1
# 13
elif items_len > actual_delete_count:
k = arr_len - actual_delete_count
while k > actual_start:
fr = unicode(k + actual_delete_count - 1)
to = unicode(k + items_len - 1)
if array.has_property(fr):
array.put(to, array.get(fr))
else:
array.delete(to)
k -= 1
# 14-17
k = actual_start
while items:
E = items.pop(0)
array.put(unicode(k), E)
k += 1
array.put('length', float(arr_len - actual_delete_count + items_len))
return A
def unshift(this, args):
array = to_object(this, args.space)
arr_len = js_arr_length(array)
argCount = len(args)
k = arr_len
while k > 0:
fr = unicode(k - 1)
to = unicode(k + argCount - 1)
if array.has_property(fr):
array.put(to, array.get(fr))
else:
array.delete(to)
k -= 1
items = tuple(args)
for j, e in enumerate(items):
array.put(unicode(j), e)
array.put('length', float(arr_len + argCount))
return float(arr_len + argCount)
def indexOf(this, args):
array = to_object(this, args.space)
searchElement = get_arg(args, 0)
arr_len = js_arr_length(array)
if arr_len == 0:
return -1.
if len(args) > 1:
n = to_int(args[1])
else:
n = 0
if n >= arr_len:
return -1.
if n >= 0:
k = n
else:
k = arr_len - abs(n)
if k < 0:
k = 0
while k < arr_len:
if array.has_property(unicode(k)):
elementK = array.get(unicode(k))
if strict_equality_op(searchElement, elementK):
return float(k)
k += 1
return -1.
def lastIndexOf(this, args):
array = to_object(this, args.space)
searchElement = get_arg(args, 0)
arr_len = js_arr_length(array)
if arr_len == 0:
return -1.
if len(args) > 1:
n = to_int(args[1])
else:
n = arr_len - 1
if n >= 0:
k = min(n, arr_len - 1)
else:
k = arr_len - abs(n)
while k >= 0:
if array.has_property(unicode(k)):
elementK = array.get(unicode(k))
if strict_equality_op(searchElement, elementK):
return float(k)
k -= 1
return -1.
def every(this, args):
array = to_object(this, args.space)
callbackfn = get_arg(args, 0)
arr_len = js_arr_length(array)
if not is_callable(callbackfn):
raise MakeError('TypeError', 'callbackfn must be a function')
T = get_arg(args, 1)
k = 0
while k < arr_len:
if array.has_property(unicode(k)):
kValue = array.get(unicode(k))
if not to_boolean(
callbackfn.call(T, (kValue, float(k), array))):
return False
k += 1
return True
def some(this, args):
array = to_object(this, args.space)
callbackfn = get_arg(args, 0)
arr_len = js_arr_length(array)
if not is_callable(callbackfn):
raise MakeError('TypeError', 'callbackfn must be a function')
T = get_arg(args, 1)
k = 0
while k < arr_len:
if array.has_property(unicode(k)):
kValue = array.get(unicode(k))
if to_boolean(callbackfn.call(T, (kValue, float(k), array))):
return True
k += 1
return False
def forEach(this, args):
array = to_object(this, args.space)
callbackfn = get_arg(args, 0)
arr_len = js_arr_length(array)
if not is_callable(callbackfn):
raise MakeError('TypeError', 'callbackfn must be a function')
_this = get_arg(args, 1)
k = 0
while k < arr_len:
sk = unicode(k)
if array.has_property(sk):
kValue = array.get(sk)
callbackfn.call(_this, (kValue, float(k), array))
k += 1
return undefined
def map(this, args):
array = to_object(this, args.space)
callbackfn = get_arg(args, 0)
arr_len = js_arr_length(array)
if not is_callable(callbackfn):
raise MakeError('TypeError', 'callbackfn must be a function')
_this = get_arg(args, 1)
k = 0
A = args.space.NewArray(0)
while k < arr_len:
Pk = unicode(k)
if array.has_property(Pk):
kValue = array.get(Pk)
mappedValue = callbackfn.call(_this, (kValue, float(k), array))
A.define_own_property(
Pk, {
'value': mappedValue,
'writable': True,
'enumerable': True,
'configurable': True
}, False)
k += 1
return A
def filter(this, args):
array = to_object(this, args.space)
callbackfn = get_arg(args, 0)
arr_len = js_arr_length(array)
if not is_callable(callbackfn):
raise MakeError('TypeError', 'callbackfn must be a function')
_this = get_arg(args, 1)
k = 0
res = []
while k < arr_len:
if array.has_property(unicode(k)):
kValue = array.get(unicode(k))
if to_boolean(
callbackfn.call(_this, (kValue, float(k), array))):
res.append(kValue)
k += 1
return args.space.ConstructArray(res)
def reduce(this, args):
array = to_object(this, args.space)
callbackfn = get_arg(args, 0)
arr_len = js_arr_length(array)
if not is_callable(callbackfn):
raise MakeError('TypeError', 'callbackfn must be a function')
if not arr_len and len(args) < 2:
raise MakeError('TypeError',
'Reduce of empty array with no initial value')
k = 0
accumulator = undefined
if len(args) > 1: # initial value present
accumulator = args[1]
else:
kPresent = False
while not kPresent and k < arr_len:
kPresent = array.has_property(unicode(k))
if kPresent:
accumulator = array.get(unicode(k))
k += 1
if not kPresent:
raise MakeError('TypeError',
'Reduce of empty array with no initial value')
while k < arr_len:
if array.has_property(unicode(k)):
kValue = array.get(unicode(k))
accumulator = callbackfn.call(
undefined, (accumulator, kValue, float(k), array))
k += 1
return accumulator
def reduceRight(this, args):
array = to_object(this, args.space)
callbackfn = get_arg(args, 0)
arr_len = js_arr_length(array)
if not is_callable(callbackfn):
raise MakeError('TypeError', 'callbackfn must be a function')
if not arr_len and len(args) < 2:
raise MakeError('TypeError',
'Reduce of empty array with no initial value')
k = arr_len - 1
accumulator = undefined
if len(args) > 1: # initial value present
accumulator = args[1]
else:
kPresent = False
while not kPresent and k >= 0:
kPresent = array.has_property(unicode(k))
if kPresent:
accumulator = array.get(unicode(k))
k -= 1
if not kPresent:
raise MakeError('TypeError',
'Reduce of empty array with no initial value')
while k >= 0:
if array.has_property(unicode(k)):
kValue = array.get(unicode(k))
accumulator = callbackfn.call(
undefined, (accumulator, kValue, float(k), array))
k -= 1
return accumulator
def sort_compare(a, b, comp):
if a is None:
if b is None:
return 0
return 1
if b is None:
if a is None:
return 0
return -1
if is_undefined(a):
if is_undefined(b):
return 0
return 1
if is_undefined(b):
if is_undefined(a):
return 0
return -1
if comp is not None:
res = comp.call(undefined, (a, b))
return to_int(res)
x, y = to_string(a), to_string(b)
if x < y:
return -1
elif x > y:
return 1
return 0
@@ -1,22 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
class BooleanPrototype:
def toString(this, args):
if GetClass(this) != 'Boolean':
raise MakeError('TypeError',
'Boolean.prototype.toString is not generic')
if is_object(this):
this = this.value
return u'true' if this else u'false'
def valueOf(this, args):
if GetClass(this) != 'Boolean':
raise MakeError('TypeError',
'Boolean.prototype.valueOf is not generic')
if is_object(this):
this = this.value
return this
-15
View File
@@ -1,15 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
class ErrorPrototype:
def toString(this, args):
if Type(this) != 'Object':
raise MakeError('TypeError',
'Error.prototype.toString called on non-object')
name = this.get('name')
name = u'Error' if is_undefined(name) else to_string(name)
msg = this.get('message')
msg = '' if is_undefined(msg) else to_string(msg)
return name + (name and msg and ': ') + msg
@@ -1,61 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
# python 3 support
import six
if six.PY3:
basestring = str
long = int
xrange = range
unicode = str
# todo fix apply and bind
class FunctionPrototype:
def toString(this, args):
if not is_callable(this):
raise MakeError('TypeError',
'Function.prototype.toString is not generic')
args = u', '.join(map(unicode, this.params))
return u'function %s(%s) { [native code] }' % (this.name if this.name
else u'', args)
def call(this, args):
if not is_callable(this):
raise MakeError('TypeError',
'Function.prototype.call is not generic')
_this = get_arg(args, 0)
_args = tuple(args)[1:]
return this.call(_this, _args)
def apply(this, args):
if not is_callable(this):
raise MakeError('TypeError',
'Function.prototype.apply is not generic')
_args = get_arg(args, 1)
if not is_object(_args):
raise MakeError(
'TypeError',
'argList argument to Function.prototype.apply must an Object')
_this = get_arg(args, 0)
return this.call(_this, js_array_to_tuple(_args))
def bind(this, args):
if not is_callable(this):
raise MakeError('TypeError',
'Function.prototype.bind is not generic')
bound_this = get_arg(args, 0)
bound_args = tuple(args)[1:]
def bound(dummy_this, extra_args):
return this.call(bound_this, bound_args + tuple(extra_args))
js_bound = args.space.NewFunction(bound, this.ctx, (), u'', False, ())
js_bound.put(u'length',
float(max(len(this.params) - len(bound_args), 0.)))
js_bound.put(u'name', u'boundFunc')
return js_bound
-205
View File
@@ -1,205 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
from ..operations import strict_equality_op
import json
indent = ''
# python 3 support
import six
if six.PY3:
basestring = str
long = int
xrange = range
unicode = str
def parse(this, args):
text, reviver = get_arg(args, 0), get_arg(args, 1)
s = to_string(text)
try:
unfiltered = json.loads(s)
except:
raise MakeError(
'SyntaxError',
'JSON.parse could not parse JSON string - Invalid syntax')
unfiltered = to_js(unfiltered, args.space)
if is_callable(reviver):
root = args.space.ConstructObject({'': unfiltered})
return walk(root, '', reviver)
else:
return unfiltered
def stringify(this, args):
global indent
value, replacer, space = get_arg(args, 0), get_arg(args, 1), get_arg(
args, 2)
stack = set([])
indent = ''
property_list = replacer_function = undefined
if is_object(replacer):
if is_callable(replacer):
replacer_function = replacer
elif replacer.Class == 'Array':
property_list = []
for e in replacer:
v = replacer[e]
item = undefined
typ = Type(v)
if typ == 'Number':
item = to_string(v)
elif typ == 'String':
item = v
elif typ == 'Object':
if GetClass(v) in ('String', 'Number'):
item = to_string(v)
if not is_undefined(item) and item not in property_list:
property_list.append(item)
if is_object(space):
if GetClass(space) == 'Number':
space = to_number(space)
elif GetClass(space) == 'String':
space = to_string(space)
if Type(space) == 'Number':
space = min(10, to_int(space))
gap = max(int(space), 0) * ' '
elif Type(space) == 'String':
gap = space[:10]
else:
gap = ''
return Str('', args.space.ConstructObject({
'': value
}), replacer_function, property_list, gap, stack, space)
def Str(key, holder, replacer_function, property_list, gap, stack, space):
value = holder.get(key)
if is_object(value):
to_json = value.get('toJSON')
if is_callable(to_json):
value = to_json.call(value, (key, ))
if not is_undefined(replacer_function):
value = replacer_function.call(holder, (key, value))
if is_object(value):
if value.Class == 'String':
value = to_string(value)
elif value.Class == 'Number':
value = to_number(value)
elif value.Class == 'Boolean':
value = to_boolean(value)
typ = Type(value)
if is_null(value):
return 'null'
elif typ == 'Boolean':
return 'true' if value else 'false'
elif typ == 'String':
return Quote(value)
elif typ == 'Number':
if not is_infinity(value):
return to_string(value)
return 'null'
if is_object(value) and not is_callable(value):
if value.Class == 'Array':
return ja(value, stack, gap, property_list, replacer_function,
space)
else:
return jo(value, stack, gap, property_list, replacer_function,
space)
return undefined
def jo(value, stack, gap, property_list, replacer_function, space):
global indent
if value in stack:
raise MakeError('TypeError', 'Converting circular structure to JSON')
stack.add(value)
stepback = indent
indent += gap
if not is_undefined(property_list):
k = property_list
else:
k = [unicode(e) for e, d in value.own.items() if d.get('enumerable')]
partial = []
for p in k:
str_p = Str(p, value, replacer_function, property_list, gap, stack,
space)
if not is_undefined(str_p):
member = json.dumps(p) + ':' + (
' ' if gap else
'') + str_p # todo not sure here - what space character?
partial.append(member)
if not partial:
final = '{}'
else:
if not gap:
final = '{%s}' % ','.join(partial)
else:
sep = ',\n' + indent
properties = sep.join(partial)
final = '{\n' + indent + properties + '\n' + stepback + '}'
stack.remove(value)
indent = stepback
return final
def ja(value, stack, gap, property_list, replacer_function, space):
global indent
if value in stack:
raise MakeError('TypeError', 'Converting circular structure to JSON')
stack.add(value)
stepback = indent
indent += gap
partial = []
length = js_arr_length(value)
for index in xrange(length):
index = unicode(index)
str_index = Str(index, value, replacer_function, property_list, gap,
stack, space)
if is_undefined(str_index):
partial.append('null')
else:
partial.append(str_index)
if not partial:
final = '[]'
else:
if not gap:
final = '[%s]' % ','.join(partial)
else:
sep = ',\n' + indent
properties = sep.join(partial)
final = '[\n' + indent + properties + '\n' + stepback + ']'
stack.remove(value)
indent = stepback
return final
def Quote(string):
return json.dumps(string)
def to_js(d, _args_space):
return convert_to_js_type(d, _args_space)
def walk(holder, name, reviver):
val = holder.get(name)
if GetClass(val) == 'Array':
for i in xrange(js_arr_length(val)):
i = unicode(i)
new_element = walk(val, i, reviver)
if is_undefined(new_element):
val.delete(i)
else:
new_element.put(i, new_element)
elif is_object(val):
for key in [
unicode(e) for e, d in val.own.items() if d.get('enumerable')
]:
new_element = walk(val, key, reviver)
if is_undefined(new_element):
val.delete(key)
else:
val.put(key, new_element)
return reviver.call(holder, (name, val))
-163
View File
@@ -1,163 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
import six
if six.PY3:
basestring = str
long = int
xrange = range
unicode = str
RADIX_SYMBOLS = {
0: '0',
1: '1',
2: '2',
3: '3',
4: '4',
5: '5',
6: '6',
7: '7',
8: '8',
9: '9',
10: 'a',
11: 'b',
12: 'c',
13: 'd',
14: 'e',
15: 'f',
16: 'g',
17: 'h',
18: 'i',
19: 'j',
20: 'k',
21: 'l',
22: 'm',
23: 'n',
24: 'o',
25: 'p',
26: 'q',
27: 'r',
28: 's',
29: 't',
30: 'u',
31: 'v',
32: 'w',
33: 'x',
34: 'y',
35: 'z'
}
def to_str_rep(num):
if is_nan(num):
return 'NaN'
elif is_infinity(num):
sign = '-' if num < 0 else ''
return sign + 'Infinity'
elif int(num) == num: # dont print .0
return unicode(int(num))
return unicode(num) # todo: Make it 100% consistent with Node
class NumberPrototype:
def toString(this, args):
if GetClass(this) != 'Number':
raise MakeError('TypeError',
'Number.prototype.valueOf is not generic')
if type(this) != float:
this = this.value
radix = get_arg(args, 0)
if is_undefined(radix):
return to_str_rep(this)
r = to_int(radix)
if r == 10:
return to_str_rep(this)
if r not in xrange(2, 37) or radix != r:
raise MakeError(
'RangeError',
'Number.prototype.toString() radix argument must be an integer between 2 and 36'
)
num = to_int(this)
if num < 0:
num = -num
sign = '-'
else:
sign = ''
res = ''
while num:
s = RADIX_SYMBOLS[num % r]
num = num // r
res = s + res
return sign + (res if res else '0')
def valueOf(this, args):
if GetClass(this) != 'Number':
raise MakeError('TypeError',
'Number.prototype.valueOf is not generic')
if type(this) != float:
this = this.value
return this
def toFixed(this, args):
if GetClass(this) != 'Number':
raise MakeError(
'TypeError',
'Number.prototype.toFixed called on incompatible receiver')
if type(this) != float:
this = this.value
fractionDigits = get_arg(args, 0)
digs = to_int(fractionDigits)
if digs < 0 or digs > 20:
raise MakeError(
'RangeError',
'toFixed() digits argument must be between 0 and 20')
elif is_infinity(this):
return 'Infinity' if this > 0 else '-Infinity'
elif is_nan(this):
return 'NaN'
return format(this, '-.%df' % digs)
def toExponential(this, args):
if GetClass(this) != 'Number':
raise MakeError(
'TypeError',
'Number.prototype.toExponential called on incompatible receiver'
)
if type(this) != float:
this = this.value
fractionDigits = get_arg(args, 0)
digs = to_int(fractionDigits)
if digs < 0 or digs > 20:
raise MakeError(
'RangeError',
'toFixed() digits argument must be between 0 and 20')
elif is_infinity(this):
return 'Infinity' if this > 0 else '-Infinity'
elif is_nan(this):
return 'NaN'
return format(this, '-.%de' % digs)
def toPrecision(this, args):
if GetClass(this) != 'Number':
raise MakeError(
'TypeError',
'Number.prototype.toPrecision called on incompatible receiver')
if type(this) != float:
this = this.value
precision = get_arg(args, 0)
if is_undefined(precision):
return to_string(this)
prec = to_int(precision)
if is_nan(this):
return 'NaN'
elif is_infinity(this):
return 'Infinity' if this > 0 else '-Infinity'
digs = prec - len(str(int(this)))
if digs >= 0:
return format(this, '-.%df' % digs)
else:
return format(this, '-.%df' % (prec - 1))
NumberPrototype.toLocaleString = NumberPrototype.toString
@@ -1,48 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
class ObjectPrototype:
def toString(this, args):
if type(this) == UNDEFINED_TYPE:
return u'[object Undefined]'
elif type(this) == NULL_TYPE:
return u'[object Null]'
return u'[object %s]' % GetClass(to_object(this, args.space))
def valueOf(this, args):
return to_object(this, args.space)
def toLocaleString(this, args):
o = to_object(this, args.space)
toString = o.get(u'toString')
if not is_callable(toString):
raise MakeError('TypeError', 'toString of this is not callcable')
else:
return toString.call(this, args)
def hasOwnProperty(this, args):
prop = get_arg(args, 0)
o = to_object(this, args.space)
return o.get_own_property(to_string(prop)) is not None
def isPrototypeOf(this, args):
# a bit stupid specification because of object conversion that will cause invalid values for primitives
# for example Object.prototype.isPrototypeOf.call((5).__proto__, 5) gives false
obj = get_arg(args, 0)
if not is_object(obj):
return False
o = to_object(this, args.space)
while 1:
obj = obj.prototype
if obj is None or is_null(obj):
return False
if obj is o:
return True
def propertyIsEnumerable(this, args):
prop = get_arg(args, 0)
o = to_object(this, args.space)
cand = o.own.get(to_string(prop))
return cand is not None and cand.get('enumerable')
@@ -1,56 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
class RegExpPrototype:
def toString(this, args):
flags = u''
try:
if this.glob:
flags += u'g'
if this.ignore_case:
flags += u'i'
if this.multiline:
flags += u'm'
except:
pass
try:
v = this.value if this.value else u'(?:)'
except:
v = u'(?:)'
return u'/%s/' % v + flags
def test(this, args):
string = get_arg(args, 0)
return RegExpExec(this, string, args.space) is not null
def _exec(
this, args
): # will be changed to exec in base.py. cant name it exec here...
string = get_arg(args, 0)
return RegExpExec(this, string, args.space)
def RegExpExec(this, string, space):
if GetClass(this) != 'RegExp':
raise MakeError('TypeError', 'RegExp.prototype.exec is not generic!')
string = to_string(string)
length = len(string)
i = to_int(this.get('lastIndex')) if this.glob else 0
matched = False
while not matched:
if i < 0 or i > length:
this.put('lastIndex', 0.)
return null
matched = this.match(string, i)
i += 1
start, end = matched.span() #[0]+i-1, matched.span()[1]+i-1
if this.glob:
this.put('lastIndex', float(end))
arr = convert_to_js_type(
[matched.group()] + list(matched.groups()), space=space)
arr.put('index', float(start))
arr.put('input', unicode(string))
return arr
-323
View File
@@ -1,323 +0,0 @@
from __future__ import unicode_literals
# -*- coding: utf-8 -*-
import re
from ..conversions import *
from ..func_utils import *
from jsregexp import RegExpExec
DIGS = set(u'0123456789')
WHITE = u"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
def replacement_template(rep, source, span, npar):
"""Takes the replacement template and some info about the match and returns filled template
"""
n = 0
res = ''
while n < len(rep) - 1:
char = rep[n]
if char == '$':
if rep[n + 1] == '$':
res += '$'
n += 2
continue
elif rep[n + 1] == '`':
# replace with string that is BEFORE match
res += source[:span[0]]
n += 2
continue
elif rep[n + 1] == '\'':
# replace with string that is AFTER match
res += source[span[1]:]
n += 2
continue
elif rep[n + 1] in DIGS:
dig = rep[n + 1]
if n + 2 < len(rep) and rep[n + 2] in DIGS:
dig += rep[n + 2]
num = int(dig)
# we will not do any replacements if we dont have this npar or dig is 0
if not num or num > len(npar):
res += '$' + dig
else:
# None - undefined has to be replaced with ''
res += npar[num - 1] if npar[num - 1] else ''
n += 1 + len(dig)
continue
res += char
n += 1
if n < len(rep):
res += rep[-1]
return res
###################################################
class StringPrototype:
def toString(this, args):
if GetClass(this) != 'String':
raise MakeError('TypeError',
'String.prototype.toString is not generic')
if type(this) == unicode:
return this
assert type(this.value) == unicode
return this.value
def valueOf(this, args):
if GetClass(this) != 'String':
raise MakeError('TypeError',
'String.prototype.valueOf is not generic')
if type(this) == unicode:
return this
assert type(this.value) == unicode
return this.value
def charAt(this, args):
cok(this)
pos = to_int(get_arg(args, 0))
s = to_string(this)
if 0 <= pos < len(s):
return s[pos]
return u''
def charCodeAt(this, args):
cok(this)
pos = to_int(get_arg(args, 0))
s = to_string(this)
if 0 <= pos < len(s):
return float(ord(s[pos]))
return NaN
def concat(this, args):
cok(this)
return to_string(this) + u''.join(map(to_string, args))
def indexOf(this, args):
cok(this)
search = to_string(get_arg(args, 0))
pos = to_int(get_arg(args, 1))
s = to_string(this)
return float(s.find(search, min(max(pos, 0), len(s))))
def lastIndexOf(this, args):
cok(this)
search = to_string(get_arg(args, 0))
pos = get_arg(args, 1)
s = to_string(this)
pos = 10**12 if is_nan(pos) else to_int(pos)
return float(s.rfind(search, 0, min(max(pos, 0) + 1, len(s))))
def localeCompare(this, args):
cok(this)
s = to_string(this)
that = to_string(get_arg(args, 0))
if s < that:
return -1.
elif s > that:
return 1.
return 0.
def match(this, args):
cok(this)
s = to_string(this)
regexp = get_arg(args, 0)
r = args.space.NewRegExp(
regexp, '') if GetClass(regexp) != 'RegExp' else regexp
if not r.glob:
return RegExpExec(r, s, space=args.space)
r.put('lastIndex', float(0))
found = []
previous_last_index = 0
last_match = True
while last_match:
result = RegExpExec(r, s, space=args.space)
if is_null(result):
last_match = False
else:
this_index = r.get('lastIndex')
if this_index == previous_last_index:
r.put('lastIndex', float(this_index + 1))
previous_last_index += 1
else:
previous_last_index = this_index
matchStr = result.get('0')
found.append(matchStr)
if not found:
return null
return args.space.ConstructArray(found)
def replace(this, args):
# VERY COMPLICATED. to check again.
cok(this)
s = to_string(this)
searchValue = get_arg(args, 0)
replaceValue = get_arg(args, 1)
res = ''
if not is_callable(replaceValue):
replaceValue = to_string(replaceValue)
func = False
else:
func = True
# Replace all ( global )
if GetClass(searchValue) == 'RegExp' and searchValue.glob:
last = 0
for e in re.finditer(searchValue.pat, s):
res += s[last:e.span()[0]]
if func:
# prepare arguments for custom func (replaceValue)
call_args = (e.group(), ) + e.groups() + (e.span()[1], s)
# convert all types to JS before Js bytecode call...
res += to_string(
replaceValue.call(
this, ensure_js_types(call_args,
space=args.space)))
else:
res += replacement_template(replaceValue, s, e.span(),
e.groups())
last = e.span()[1]
res += s[last:]
return res
elif GetClass(searchValue) == 'RegExp':
e = re.search(searchValue.pat, s)
if e is None:
return s
span = e.span()
pars = e.groups()
match = e.group()
else:
match = to_string(searchValue)
ind = s.find(match)
if ind == -1:
return s
span = ind, ind + len(match)
pars = ()
res = s[:span[0]]
if func:
call_args = (match, ) + pars + (span[1], s)
# convert all types to JS before Js bytecode call...
res += to_string(
replaceValue.call(this,
ensure_js_types(call_args,
space=args.space)))
else:
res += replacement_template(replaceValue, s, span, pars)
res += s[span[1]:]
return res
def search(this, args):
cok(this)
string = to_string(this)
regexp = get_arg(args, 0)
if GetClass(regexp) == 'RegExp':
rx = regexp
else:
rx = args.space.NewRegExp(regexp, '')
res = re.search(rx.pat, string)
if res is not None:
return float(res.span()[0])
return -1.
def slice(this, args):
cok(this)
s = to_string(this)
start = to_int(get_arg(args, 0))
length = len(s)
end = get_arg(args, 1)
end = length if is_undefined(end) else to_int(end)
#From = max(length+start, 0) if start<0 else min(length, start)
#To = max(length+end, 0) if end<0 else min(length, end)
return s[start:end]
def split(this, args):
# its a bit different from re.split!
cok(this)
s = to_string(this)
separator = get_arg(args, 0)
limit = get_arg(args, 1)
lim = 2**32 - 1 if is_undefined(limit) else to_uint32(limit)
if not lim:
return args.space.ConstructArray([])
if is_undefined(separator):
return args.space.ConstructArray([s])
len_s = len(s)
res = []
R = separator if GetClass(separator) == 'RegExp' else to_string(
separator)
if not len_s:
if SplitMatch(s, 0, R) is None:
return args.space.ConstructArray([s])
return args.space.ConstructArray([])
p = q = 0
while q != len_s:
e, cap = SplitMatch(s, q, R)
if e is None or e == p:
q += 1
continue
res.append(s[p:q])
p = q = e
if len(res) == lim:
return args.space.ConstructArray(res)
for element in cap:
res.append(element)
if len(res) == lim:
return args.space.ConstructArray(res)
res.append(s[p:])
return args.space.ConstructArray(res)
def substring(this, args):
cok(this)
s = to_string(this)
start = to_int(get_arg(args, 0))
length = len(s)
end = get_arg(args, 1)
end = length if is_undefined(end) else to_int(end)
fstart = min(max(start, 0), length)
fend = min(max(end, 0), length)
return s[min(fstart, fend):max(fstart, fend)]
def substr(this, args):
cok(this)
#I hate this function and its description in specification
r1 = to_string(this)
r2 = to_int(get_arg(args, 0))
length = get_arg(args, 1)
r3 = 10**20 if is_undefined(length) else to_int(length)
r4 = len(r1)
r5 = r2 if r2 >= 0 else max(0, r2 + r4)
r6 = min(max(r3, 0), r4 - r5)
if r6 <= 0:
return ''
return r1[r5:r5 + r6]
def toLowerCase(this, args):
cok(this)
return to_string(this).lower()
def toLocaleLowerCase(this, args):
cok(this)
return to_string(this).lower()
def toUpperCase(this, args):
cok(this)
return to_string(this).upper()
def toLocaleUpperCase(this, args):
cok(this)
return to_string(this).upper()
def trim(this, args):
cok(this)
return to_string(this).strip(WHITE)
def SplitMatch(s, q, R):
# s is Py String to match, q is the py int match start and R is Js RegExp or String.
if GetClass(R) == 'RegExp':
res = R.match(s, q)
return (None, ()) if res is None else (res.span()[1], res.groups())
# R is just a string
if s[q:].startswith(R):
return q + len(R), ()
return None, ()
-149
View File
@@ -1,149 +0,0 @@
from __future__ import unicode_literals
from ..conversions import *
from ..func_utils import *
RADIX_CHARS = {
'1': 1,
'0': 0,
'3': 3,
'2': 2,
'5': 5,
'4': 4,
'7': 7,
'6': 6,
'9': 9,
'8': 8,
'a': 10,
'c': 12,
'b': 11,
'e': 14,
'd': 13,
'g': 16,
'f': 15,
'i': 18,
'h': 17,
'k': 20,
'j': 19,
'm': 22,
'l': 21,
'o': 24,
'n': 23,
'q': 26,
'p': 25,
's': 28,
'r': 27,
'u': 30,
't': 29,
'w': 32,
'v': 31,
'y': 34,
'x': 33,
'z': 35,
'A': 10,
'C': 12,
'B': 11,
'E': 14,
'D': 13,
'G': 16,
'F': 15,
'I': 18,
'H': 17,
'K': 20,
'J': 19,
'M': 22,
'L': 21,
'O': 24,
'N': 23,
'Q': 26,
'P': 25,
'S': 28,
'R': 27,
'U': 30,
'T': 29,
'W': 32,
'V': 31,
'Y': 34,
'X': 33,
'Z': 35
}
# parseFloat
# parseInt
# isFinite
# isNaN
def parseInt(this, args):
string, radix = get_arg(args, 0), get_arg(args, 1)
string = to_string(string).lstrip()
sign = 1
if string and string[0] in ('+', '-'):
if string[0] == '-':
sign = -1
string = string[1:]
r = to_int32(radix)
strip_prefix = True
if r:
if r < 2 or r > 36:
return NaN
if r != 16:
strip_prefix = False
else:
r = 10
if strip_prefix:
if len(string) >= 2 and string[:2] in ('0x', '0X'):
string = string[2:]
r = 16
n = 0
num = 0
while n < len(string):
cand = RADIX_CHARS.get(string[n])
if cand is None or not cand < r:
break
num = cand + num * r
n += 1
if not n:
return NaN
return float(sign * num)
def parseFloat(this, args):
string = get_arg(args, 0)
string = to_string(string).strip()
sign = 1
if string and string[0] in ('+', '-'):
if string[0] == '-':
sign = -1
string = string[1:]
num = None
length = 1
max_len = None
failed = 0
while length <= len(string):
try:
num = float(string[:length])
max_len = length
failed = 0
except:
failed += 1
if failed > 4: # cant be a number anymore
break
length += 1
if num is None:
return NaN
return sign * float(string[:max_len])
def isNaN(this, args):
number = get_arg(args, 0)
if is_nan(to_number(number)):
return True
return False
def isFinite(this, args):
number = get_arg(args, 0)
num = to_number(number)
if is_nan(num) or is_infinity(num):
return False
return True
-33
View File
@@ -1,33 +0,0 @@
import pyjsparser
from .space import Space
from . import fill_space
from .byte_trans import ByteCodeGenerator
from .code import Code
from .simplex import *
pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError(u'SyntaxError', unicode(msg))
def get_js_bytecode(js):
a = ByteCodeGenerator(Code())
d = pyjsparser.parse(js)
a.emit(d)
return a.exe.tape
def eval_js_vm(js, debug=False):
a = ByteCodeGenerator(Code(debug_mode=debug))
s = Space()
a.exe.space = s
s.exe = a.exe
d = pyjsparser.parse(js)
a.emit(d)
fill_space.fill_space(s, a)
if debug:
from pprint import pprint
pprint(a.exe.tape)
print()
a.exe.compile()
return a.exe.run(a.exe.space.GlobalObj)
-160
View File
@@ -1,160 +0,0 @@
from __future__ import unicode_literals
import six
if six.PY3:
basestring = str
long = int
xrange = range
unicode = str
#Undefined
class PyJsUndefined(object):
TYPE = 'Undefined'
Class = 'Undefined'
undefined = PyJsUndefined()
#Null
class PyJsNull(object):
TYPE = 'Null'
Class = 'Null'
null = PyJsNull()
Infinity = float('inf')
NaN = float('nan')
UNDEFINED_TYPE = PyJsUndefined
NULL_TYPE = PyJsNull
STRING_TYPE = unicode if six.PY2 else str
NUMBER_TYPE = float
BOOLEAN_TYPE = bool
# exactly 5 simplexes!
PRIMITIVES = frozenset(
[UNDEFINED_TYPE, NULL_TYPE, STRING_TYPE, NUMBER_TYPE, BOOLEAN_TYPE])
TYPE_NAMES = {
UNDEFINED_TYPE: 'Undefined',
NULL_TYPE: 'Null',
STRING_TYPE: 'String',
NUMBER_TYPE: 'Number',
BOOLEAN_TYPE: 'Boolean',
}
def Type(x):
# Any -> Str
return TYPE_NAMES.get(type(x), 'Object')
def GetClass(x):
# Any -> Str
cand = TYPE_NAMES.get(type(x))
if cand is None:
return x.Class
return cand
def is_undefined(self):
return self is undefined
def is_null(self):
return self is null
def is_primitive(self):
return type(self) in PRIMITIVES
def is_object(self):
return not is_primitive(self)
def is_callable(self):
return hasattr(self, 'call')
def is_infinity(self):
return self == Infinity or self == -Infinity
def is_nan(self):
return self != self # nan!=nan evaluates to True
def is_finite(self):
return not (is_nan(self) or is_infinity(self))
class JsException(Exception):
def __init__(self, typ=None, message=None, throw=None):
if typ is None and message is None and throw is None:
# it means its the trasnlator based error (old format), do nothing
self._translator_based = True
else:
assert throw is None or (typ is None
and message is None), (throw, typ,
message)
self._translator_based = False
self.typ = typ
self.message = message
self.throw = throw
def get_thrown_value(self, space):
if self.throw is not None:
return self.throw
else:
return space.NewError(self.typ, self.message)
def __str__(self):
if self._translator_based:
if self.mes.Class == 'Error':
return self.mes.callprop('toString').value
else:
return self.mes.to_string().value
else:
if self.throw is not None:
from .conversions import to_string
return to_string(self.throw)
else:
return self.typ + ': ' + self.message
def MakeError(typ, message=u'no info', throw=None):
return JsException(typ,
unicode(message) if message is not None else message,
throw)
def value_from_js_exception(js_exception, space):
if js_exception.throw is not None:
return js_exception.throw
else:
return space.NewError(js_exception.typ, js_exception.message)
def js_dtoa(number):
if is_nan(number):
return u'NaN'
elif is_infinity(number):
if number > 0:
return u'Infinity'
return u'-Infinity'
elif number == 0.:
return u'0'
elif abs(number) < 1e-6 or abs(number) >= 1e21:
frac, exponent = unicode(repr(float(number))).split('e')
# Remove leading zeros from the exponent.
exponent = int(exponent)
return frac + ('e' if exponent < 0 else 'e+') + unicode(exponent)
elif abs(number) < 1e-4: # python starts to return exp notation while we still want the prec
frac, exponent = unicode(repr(float(number))).split('e-')
base = u'0.' + u'0' * (int(exponent) - 1) + frac.lstrip('-').replace('.', '')
return base if number > 0. else u'-' + base
elif isinstance(number, long) or number.is_integer(): # dont print .0
return unicode(int(number))
return unicode(repr(number)) # python representation should be equivalent.
-92
View File
@@ -1,92 +0,0 @@
from .base import *
from .simplex import *
class Space(object):
def __init__(self):
self.GlobalObj = None
self.ctx = None
self.byte_generator = None
self.Number = None
self.String = None
self.Boolean = None
self.RegExp = None
self.Object = None
self.Array = None
self.Function = None
self.BooleanPrototype = None
self.NumberPrototype = None
self.StringPrototype = None
self.FunctionPrototype = None
self.ArrayPrototype = None
self.RegExpPrototype = None
self.DatePrototype = None
self.ObjectPrototype = None
self.ErrorPrototype = None
self.EvalErrorPrototype = None
self.RangeErrorPrototype = None
self.ReferenceErrorPrototype = None
self.SyntaxErrorPrototype = None
self.TypeErrorPrototype = None
self.URIErrorPrototype = None
self.interpreter = None
@property
def ERROR_TYPES(self):
return {
'Error': self.ErrorPrototype,
'EvalError': self.EvalErrorPrototype,
'RangeError': self.RangeErrorPrototype,
'ReferenceError': self.ReferenceErrorPrototype,
'SyntaxError': self.SyntaxErrorPrototype,
'TypeError': self.TypeErrorPrototype,
'URIError': self.URIErrorPrototype,
}
def get_global_environment(self):
return self.GlobalCtx.variable_environment()
def NewObject(self):
return PyJsObject(self.ObjectPrototype)
def NewFunction(self, code, ctx, params, name, is_declaration,
definitions):
return PyJsFunction(
code,
ctx,
params,
name,
self,
is_declaration,
definitions,
prototype=self.FunctionPrototype)
def NewDate(self, value):
return PyJsDate(value, self.DatePrototype)
def NewArray(self, length=0):
return PyJsArray(length, self.ArrayPrototype)
def NewError(self, typ, message):
return PyJsError(message, self.ERROR_TYPES[typ])
def NewRegExp(self, body, flags):
return PyJsRegExp(body, flags, self.RegExpPrototype)
def ConstructArray(self, py_arr):
''' note py_arr elems are NOT converted to PyJs types!'''
arr = self.NewArray(len(py_arr))
arr._init(py_arr)
return arr
def ConstructObject(self, py_obj):
''' note py_obj items are NOT converted to PyJs types! '''
obj = self.NewObject()
for k, v in py_obj.items():
obj.put(unicode(k), v)
return obj
-62
View File
@@ -1,62 +0,0 @@
from timeit import timeit
from collections import namedtuple
from array import array
from itertools import izip
from collections import deque
class Y(object):
UUU = 88
def __init__(self, x):
self.x = x
def s(self, x):
return self.x + 1
class X(Y):
A = 10
B = 2
C = 4
D = 9
def __init__(self, x):
self.x = x
self.stack = []
self.par = super(X, self)
def s(self, x):
pass
def __add__(self, other):
return self.x + other.x
def another(self):
return Y.s(self, 1)
def yet_another(self):
return self.par.s(1)
def add(a, b):
return a.x + b.x
t = []
Type = None
try:
print timeit(
"""
t.append(4)
t.pop()
""",
"from __main__ import X,Y,namedtuple,array,t,add,Type, izip",
number=1000000)
except:
raise
-35
View File
@@ -1,35 +0,0 @@
import six
if six.PY3:
basestring = str
long = int
xrange = range
unicode = str
def to_key(literal_or_identifier):
''' returns string representation of this object'''
if literal_or_identifier['type'] == 'Identifier':
return literal_or_identifier['name']
elif literal_or_identifier['type'] == 'Literal':
k = literal_or_identifier['value']
if isinstance(k, float):
return unicode(float_repr(k))
elif 'regex' in literal_or_identifier:
return compose_regex(k)
elif isinstance(k, bool):
return u'true' if k else u'false'
elif k is None:
return u'null'
else:
return unicode(k)
def compose_regex(val):
reg, flags = val
# reg = REGEXP_CONVERTER._unescape_string(reg)
return u'/%s/%s' % (reg, flags)
def float_repr(f):
if int(f) == f:
return unicode(repr(int(f)))
return unicode(repr(f))