Aggiornata libreria js2py
This commit is contained in:
+11
-8
@@ -126,7 +126,7 @@ def HJs(val):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
message = 'your Python function failed! '
|
message = 'your Python function failed! '
|
||||||
try:
|
try:
|
||||||
message += e.message
|
message += str(e)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
raise MakeError('Error', message)
|
raise MakeError('Error', message)
|
||||||
@@ -319,7 +319,7 @@ class PyJs(object):
|
|||||||
#prop = prop.value
|
#prop = prop.value
|
||||||
if self.Class == 'Undefined' or self.Class == 'Null':
|
if self.Class == 'Undefined' or self.Class == 'Null':
|
||||||
raise MakeError('TypeError',
|
raise MakeError('TypeError',
|
||||||
'Undefined and null dont have properties!')
|
'Undefined and null dont have properties (tried getting property %s)' % repr(prop))
|
||||||
if not isinstance(prop, basestring):
|
if not isinstance(prop, basestring):
|
||||||
prop = prop.to_string().value
|
prop = prop.to_string().value
|
||||||
if not isinstance(prop, basestring): raise RuntimeError('Bug')
|
if not isinstance(prop, basestring): raise RuntimeError('Bug')
|
||||||
@@ -361,7 +361,7 @@ class PyJs(object):
|
|||||||
* / % + - << >> & ^ |'''
|
* / % + - << >> & ^ |'''
|
||||||
if self.Class == 'Undefined' or self.Class == 'Null':
|
if self.Class == 'Undefined' or self.Class == 'Null':
|
||||||
raise MakeError('TypeError',
|
raise MakeError('TypeError',
|
||||||
'Undefined and null dont have properties!')
|
'Undefined and null don\'t have properties (tried setting property %s)' % repr(prop))
|
||||||
if not isinstance(prop, basestring):
|
if not isinstance(prop, basestring):
|
||||||
prop = prop.to_string().value
|
prop = prop.to_string().value
|
||||||
if NUMPY_AVAILABLE and prop.isdigit():
|
if NUMPY_AVAILABLE and prop.isdigit():
|
||||||
@@ -991,7 +991,8 @@ class PyJs(object):
|
|||||||
cand = self.get(prop)
|
cand = self.get(prop)
|
||||||
if not cand.is_callable():
|
if not cand.is_callable():
|
||||||
raise MakeError('TypeError',
|
raise MakeError('TypeError',
|
||||||
'%s is not a function' % cand.typeof())
|
'%s is not a function (tried calling property %s of %s)' % (
|
||||||
|
cand.typeof(), repr(prop), repr(self.Class)))
|
||||||
return cand.call(self, args)
|
return cand.call(self, args)
|
||||||
|
|
||||||
def to_python(self):
|
def to_python(self):
|
||||||
@@ -1304,7 +1305,7 @@ class PyObjectWrapper(PyJs):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
message = 'your Python function failed! '
|
message = 'your Python function failed! '
|
||||||
try:
|
try:
|
||||||
message += e.message
|
message += str(e)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
raise MakeError('Error', message)
|
raise MakeError('Error', message)
|
||||||
@@ -1464,9 +1465,11 @@ class PyJsFunction(PyJs):
|
|||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
raise
|
raise
|
||||||
except RuntimeError as e: # maximum recursion
|
except RuntimeError as e: # maximum recursion
|
||||||
raise MakeError(
|
try:
|
||||||
'RangeError', e.message if
|
msg = e.message
|
||||||
not isinstance(e, NotImplementedError) else 'Not implemented!')
|
except:
|
||||||
|
msg = repr(e)
|
||||||
|
raise MakeError('RangeError', msg)
|
||||||
|
|
||||||
def has_instance(self, other):
|
def has_instance(self, other):
|
||||||
# I am not sure here so instanceof may not work lol.
|
# I am not sure here so instanceof may not work lol.
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this
|
|||||||
mili = args[6].to_number() if l > 6 else Js(0)
|
mili = args[6].to_number() if l > 6 else Js(0)
|
||||||
if not y.is_nan() and 0 <= y.value <= 99:
|
if not y.is_nan() and 0 <= y.value <= 99:
|
||||||
y = y + Js(1900)
|
y = y + Js(1900)
|
||||||
t = TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
|
return TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
|
||||||
return PyJsDate(t, prototype=DatePrototype)
|
|
||||||
|
|
||||||
|
|
||||||
@Js
|
@Js
|
||||||
@@ -76,11 +75,12 @@ class PyJsDate(PyJs):
|
|||||||
|
|
||||||
# todo fix this problematic datetime part
|
# todo fix this problematic datetime part
|
||||||
def to_local_dt(self):
|
def to_local_dt(self):
|
||||||
return datetime.datetime.utcfromtimestamp(
|
return datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||||
UTCToLocal(self.value) // 1000)
|
seconds=UTCToLocal(self.value) // 1000)
|
||||||
|
|
||||||
def to_utc_dt(self):
|
def to_utc_dt(self):
|
||||||
return datetime.datetime.utcfromtimestamp(self.value // 1000)
|
return datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||||
|
seconds=self.value // 1000)
|
||||||
|
|
||||||
def local_strftime(self, pattern):
|
def local_strftime(self, pattern):
|
||||||
if self.value is NaN:
|
if self.value is NaN:
|
||||||
@@ -118,20 +118,39 @@ class PyJsDate(PyJs):
|
|||||||
|
|
||||||
|
|
||||||
def parse_date(py_string): # todo support all date string formats
|
def parse_date(py_string): # todo support all date string formats
|
||||||
|
date_formats = (
|
||||||
|
"%Y-%m-%d",
|
||||||
|
"%m/%d/%Y",
|
||||||
|
"%b %d %Y",
|
||||||
|
)
|
||||||
|
# Supports these hour formats and with or hour.
|
||||||
|
hour_formats = (
|
||||||
|
"T%H:%M:%S.%f",
|
||||||
|
"T%H:%M:%S",
|
||||||
|
) + ('',)
|
||||||
|
# Supports with or without Z indicator.
|
||||||
|
z_formats = ("Z",) + ('',)
|
||||||
|
supported_formats = [
|
||||||
|
d + t + z
|
||||||
|
for d in date_formats
|
||||||
|
for t in hour_formats
|
||||||
|
for z in z_formats
|
||||||
|
]
|
||||||
|
for date_format in supported_formats:
|
||||||
try:
|
try:
|
||||||
try:
|
dt = datetime.datetime.strptime(py_string, date_format)
|
||||||
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%S.%fZ")
|
except ValueError:
|
||||||
except:
|
continue
|
||||||
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%SZ")
|
else:
|
||||||
return MakeDate(
|
return MakeDate(
|
||||||
MakeDay(Js(dt.year), Js(dt.month - 1), Js(dt.day)),
|
MakeDay(Js(dt.year), Js(dt.month - 1), Js(dt.day)),
|
||||||
MakeTime(
|
MakeTime(
|
||||||
Js(dt.hour), Js(dt.minute), Js(dt.second),
|
Js(dt.hour), Js(dt.minute), Js(dt.second),
|
||||||
Js(dt.microsecond // 1000)))
|
Js(dt.microsecond // 1000)))
|
||||||
except:
|
|
||||||
raise MakeError(
|
raise MakeError(
|
||||||
'TypeError',
|
'TypeError',
|
||||||
'Could not parse date %s - unsupported date format. Currently only supported format is RFC3339 utc. Sorry!'
|
'Could not parse date %s - unsupported date format. Currently only supported formats are RFC3339 utc, ISO Date, Short Date, and Long Date. Sorry!'
|
||||||
% py_string)
|
% py_string)
|
||||||
|
|
||||||
|
|
||||||
@@ -332,7 +351,7 @@ class DateProto:
|
|||||||
check_date(this)
|
check_date(this)
|
||||||
t = UTCToLocal(this.value)
|
t = UTCToLocal(this.value)
|
||||||
tim = MakeTime(
|
tim = MakeTime(
|
||||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms)
|
||||||
u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
|
u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
|
||||||
this.value = u
|
this.value = u
|
||||||
return u
|
return u
|
||||||
@@ -341,12 +360,164 @@ class DateProto:
|
|||||||
check_date(this)
|
check_date(this)
|
||||||
t = this.value
|
t = this.value
|
||||||
tim = MakeTime(
|
tim = MakeTime(
|
||||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms)
|
||||||
u = TimeClip(MakeDate(Day(t), tim))
|
u = TimeClip(MakeDate(Day(t), tim))
|
||||||
this.value = u
|
this.value = u
|
||||||
return u
|
return u
|
||||||
|
|
||||||
# todo Complete all setters!
|
def setSeconds(sec, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(
|
||||||
|
Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli))
|
||||||
|
u = TimeClip(LocalToUTC(date))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCSeconds(sec, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(
|
||||||
|
Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli))
|
||||||
|
v = TimeClip(date)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setMinutes(min, sec=None, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
m = min.to_number()
|
||||||
|
if not sec is None: s = Js(SecFromTime(t))
|
||||||
|
else: s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli))
|
||||||
|
u = TimeClip(LocalToUTC(date))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCMinutes(min, sec=None, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
m = min.to_number()
|
||||||
|
if not sec is None: s = Js(SecFromTime(t))
|
||||||
|
else: s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli))
|
||||||
|
v = TimeClip(date)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setHours(hour, min=None, sec=None, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
h = hour.to_number()
|
||||||
|
if not min is None: m = Js(MinFromTime(t))
|
||||||
|
else: m = min.to_number()
|
||||||
|
if not sec is None: s = Js(SecFromTime(t))
|
||||||
|
else: s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(Day(t), MakeTime(h, m, s, milli))
|
||||||
|
u = TimeClip(LocalToUTC(date))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCHours(hour, min=None, sec=None, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
h = hour.to_number()
|
||||||
|
if not min is None: m = Js(MinFromTime(t))
|
||||||
|
else: m = min.to_number()
|
||||||
|
if not sec is None: s = Js(SecFromTime(t))
|
||||||
|
else: s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(Day(t), MakeTime(h, m, s, milli))
|
||||||
|
v = TimeClip(date)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setDate(date):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t))
|
||||||
|
u = TimeClip(LocalToUTC(newDate))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCDate(date):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t))
|
||||||
|
v = TimeClip(newDate)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setMonth(month, date=None):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
m = month.to_number()
|
||||||
|
if not date is None: dt = Js(DateFromTime(t))
|
||||||
|
else: dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t))
|
||||||
|
u = TimeClip(LocalToUTC(newDate))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCMonth(month, date=None):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
m = month.to_number()
|
||||||
|
if not date is None: dt = Js(DateFromTime(t))
|
||||||
|
else: dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t))
|
||||||
|
v = TimeClip(newDate)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setFullYear(year, month=None, date=None):
|
||||||
|
check_date(this)
|
||||||
|
if not this.value is NaN: t = UTCToLocal(this.value)
|
||||||
|
else: t = 0
|
||||||
|
y = year.to_number()
|
||||||
|
if not month is None: m = Js(MonthFromTime(t))
|
||||||
|
else: m = month.to_number()
|
||||||
|
if not date is None: dt = Js(DateFromTime(t))
|
||||||
|
else: dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(y, m, dt), TimeWithinDay(t))
|
||||||
|
u = TimeClip(LocalToUTC(newDate))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCFullYear(year, month=None, date=None):
|
||||||
|
check_date(this)
|
||||||
|
if not this.value is NaN: t = UTCToLocal(this.value)
|
||||||
|
else: t = 0
|
||||||
|
y = year.to_number()
|
||||||
|
if not month is None: m = Js(MonthFromTime(t))
|
||||||
|
else: m = month.to_number()
|
||||||
|
if not date is None: dt = Js(DateFromTime(t))
|
||||||
|
else: dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(y, m, dt), TimeWithinDay(t))
|
||||||
|
v = TimeClip(newDate)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
def toUTCString():
|
def toUTCString():
|
||||||
check_date(this)
|
check_date(this)
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ def DaylightSavingTA(t):
|
|||||||
return t
|
return t
|
||||||
try:
|
try:
|
||||||
return int(
|
return int(
|
||||||
LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(
|
LOCAL_ZONE.dst(datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||||
t // 1000)).seconds) * 1000
|
seconds=t // 1000)).seconds) * 1000
|
||||||
except:
|
except:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Invalid datetime date, assumed DST time, may be inaccurate...',
|
'Invalid datetime date, assumed DST time, may be inaccurate...',
|
||||||
|
|||||||
+5
-12
@@ -53,7 +53,7 @@ def write_file_contents(path_or_file, contents):
|
|||||||
if hasattr(path_or_file, 'write'):
|
if hasattr(path_or_file, 'write'):
|
||||||
path_or_file.write(contents)
|
path_or_file.write(contents)
|
||||||
else:
|
else:
|
||||||
with open(path_as_local(path_or_file), 'w') as f:
|
with codecs.open(path_as_local(path_or_file), "w", "utf-8") as f:
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
|
|
||||||
|
|
||||||
@@ -238,6 +238,10 @@ class EvalJs(object):
|
|||||||
self.execute_debug(code)
|
self.execute_debug(code)
|
||||||
return self['PyJsEvalResult']
|
return self['PyJsEvalResult']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def context(self):
|
||||||
|
return self._context
|
||||||
|
|
||||||
def __getattr__(self, var):
|
def __getattr__(self, var):
|
||||||
return getattr(self._var, var)
|
return getattr(self._var, var)
|
||||||
|
|
||||||
@@ -268,14 +272,3 @@ class EvalJs(object):
|
|||||||
else:
|
else:
|
||||||
sys.stderr.write('EXCEPTION: ' + str(e) + '\n')
|
sys.stderr.write('EXCEPTION: ' + str(e) + '\n')
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
|
||||||
#print x
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
#with open('C:\Users\Piotrek\Desktop\esprima.js', 'rb') as f:
|
|
||||||
# x = f.read()
|
|
||||||
e = EvalJs()
|
|
||||||
e.execute('square(x)')
|
|
||||||
#e.execute(x)
|
|
||||||
e.console()
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ def console():
|
|||||||
|
|
||||||
@Js
|
@Js
|
||||||
def log():
|
def log():
|
||||||
print(arguments[0])
|
print(" ".join(repr(element) for element in arguments.to_list()))
|
||||||
|
|
||||||
console.put('log', log)
|
console.put('log', log)
|
||||||
console.put('debug', log)
|
console.put('debug', log)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
from .seval import eval_js_vm
|
||||||
@@ -602,11 +602,12 @@ class PyJsDate(PyJs):
|
|||||||
|
|
||||||
# todo fix this problematic datetime part
|
# todo fix this problematic datetime part
|
||||||
def to_local_dt(self):
|
def to_local_dt(self):
|
||||||
return datetime.datetime.utcfromtimestamp(
|
return datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||||
self.UTCToLocal(self.value) // 1000)
|
seconds=self.UTCToLocal(self.value) // 1000)
|
||||||
|
|
||||||
def to_utc_dt(self):
|
def to_utc_dt(self):
|
||||||
return datetime.datetime.utcfromtimestamp(self.value // 1000)
|
return datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||||
|
seconds=self.value // 1000)
|
||||||
|
|
||||||
def local_strftime(self, pattern):
|
def local_strftime(self, pattern):
|
||||||
if self.value is NaN:
|
if self.value is NaN:
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this
|
|||||||
mili = args[6].to_number() if l > 6 else Js(0)
|
mili = args[6].to_number() if l > 6 else Js(0)
|
||||||
if not y.is_nan() and 0 <= y.value <= 99:
|
if not y.is_nan() and 0 <= y.value <= 99:
|
||||||
y = y + Js(1900)
|
y = y + Js(1900)
|
||||||
t = TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
|
return TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
|
||||||
return PyJsDate(t, prototype=DatePrototype)
|
|
||||||
|
|
||||||
|
|
||||||
@Js
|
@Js
|
||||||
@@ -76,11 +75,12 @@ class PyJsDate(PyJs):
|
|||||||
|
|
||||||
# todo fix this problematic datetime part
|
# todo fix this problematic datetime part
|
||||||
def to_local_dt(self):
|
def to_local_dt(self):
|
||||||
return datetime.datetime.utcfromtimestamp(
|
return datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||||
UTCToLocal(self.value) // 1000)
|
seconds=UTCToLocal(self.value) // 1000)
|
||||||
|
|
||||||
def to_utc_dt(self):
|
def to_utc_dt(self):
|
||||||
return datetime.datetime.utcfromtimestamp(self.value // 1000)
|
return datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||||
|
seconds=self.value // 1000)
|
||||||
|
|
||||||
def local_strftime(self, pattern):
|
def local_strftime(self, pattern):
|
||||||
if self.value is NaN:
|
if self.value is NaN:
|
||||||
@@ -332,7 +332,7 @@ class DateProto:
|
|||||||
check_date(this)
|
check_date(this)
|
||||||
t = UTCToLocal(this.value)
|
t = UTCToLocal(this.value)
|
||||||
tim = MakeTime(
|
tim = MakeTime(
|
||||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms)
|
||||||
u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
|
u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
|
||||||
this.value = u
|
this.value = u
|
||||||
return u
|
return u
|
||||||
@@ -341,12 +341,164 @@ class DateProto:
|
|||||||
check_date(this)
|
check_date(this)
|
||||||
t = this.value
|
t = this.value
|
||||||
tim = MakeTime(
|
tim = MakeTime(
|
||||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms)
|
||||||
u = TimeClip(MakeDate(Day(t), tim))
|
u = TimeClip(MakeDate(Day(t), tim))
|
||||||
this.value = u
|
this.value = u
|
||||||
return u
|
return u
|
||||||
|
|
||||||
# todo Complete all setters!
|
def setSeconds(sec, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(
|
||||||
|
Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli))
|
||||||
|
u = TimeClip(LocalToUTC(date))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCSeconds(sec, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(
|
||||||
|
Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli))
|
||||||
|
v = TimeClip(date)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setMinutes(min, sec=None, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
m = min.to_number()
|
||||||
|
if not sec is None: s = Js(SecFromTime(t))
|
||||||
|
else: s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli))
|
||||||
|
u = TimeClip(LocalToUTC(date))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCMinutes(min, sec=None, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
m = min.to_number()
|
||||||
|
if not sec is None: s = Js(SecFromTime(t))
|
||||||
|
else: s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli))
|
||||||
|
v = TimeClip(date)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setHours(hour, min=None, sec=None, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
h = hour.to_number()
|
||||||
|
if not min is None: m = Js(MinFromTime(t))
|
||||||
|
else: m = min.to_number()
|
||||||
|
if not sec is None: s = Js(SecFromTime(t))
|
||||||
|
else: s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(Day(t), MakeTime(h, m, s, milli))
|
||||||
|
u = TimeClip(LocalToUTC(date))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCHours(hour, min=None, sec=None, ms=None):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
h = hour.to_number()
|
||||||
|
if not min is None: m = Js(MinFromTime(t))
|
||||||
|
else: m = min.to_number()
|
||||||
|
if not sec is None: s = Js(SecFromTime(t))
|
||||||
|
else: s = sec.to_number()
|
||||||
|
if not ms is None: milli = Js(msFromTime(t))
|
||||||
|
else: milli = ms.to_number()
|
||||||
|
date = MakeDate(Day(t), MakeTime(h, m, s, milli))
|
||||||
|
v = TimeClip(date)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setDate(date):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t))
|
||||||
|
u = TimeClip(LocalToUTC(newDate))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCDate(date):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t))
|
||||||
|
v = TimeClip(newDate)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setMonth(month, date=None):
|
||||||
|
check_date(this)
|
||||||
|
t = UTCToLocal(this.value)
|
||||||
|
m = month.to_number()
|
||||||
|
if not date is None: dt = Js(DateFromTime(t))
|
||||||
|
else: dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t))
|
||||||
|
u = TimeClip(LocalToUTC(newDate))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCMonth(month, date=None):
|
||||||
|
check_date(this)
|
||||||
|
t = this.value
|
||||||
|
m = month.to_number()
|
||||||
|
if not date is None: dt = Js(DateFromTime(t))
|
||||||
|
else: dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t))
|
||||||
|
v = TimeClip(newDate)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
def setFullYear(year, month=None, date=None):
|
||||||
|
check_date(this)
|
||||||
|
if not this.value is NaN: t = UTCToLocal(this.value)
|
||||||
|
else: t = 0
|
||||||
|
y = year.to_number()
|
||||||
|
if not month is None: m = Js(MonthFromTime(t))
|
||||||
|
else: m = month.to_number()
|
||||||
|
if not date is None: dt = Js(DateFromTime(t))
|
||||||
|
else: dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(y, m, dt), TimeWithinDay(t))
|
||||||
|
u = TimeClip(LocalToUTC(newDate))
|
||||||
|
this.value = u
|
||||||
|
return u
|
||||||
|
|
||||||
|
def setUTCFullYear(year, month=None, date=None):
|
||||||
|
check_date(this)
|
||||||
|
if not this.value is NaN: t = UTCToLocal(this.value)
|
||||||
|
else: t = 0
|
||||||
|
y = year.to_number()
|
||||||
|
if not month is None: m = Js(MonthFromTime(t))
|
||||||
|
else: m = month.to_number()
|
||||||
|
if not date is None: dt = Js(DateFromTime(t))
|
||||||
|
else: dt = date.to_number()
|
||||||
|
newDate = MakeDate(
|
||||||
|
MakeDay(y, m, dt), TimeWithinDay(t))
|
||||||
|
v = TimeClip(newDate)
|
||||||
|
this.value = v
|
||||||
|
return v
|
||||||
|
|
||||||
def toUTCString():
|
def toUTCString():
|
||||||
check_date(this)
|
check_date(this)
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ CONSTANTS = {
|
|||||||
'SQRT1_2': 0.7071067811865476,
|
'SQRT1_2': 0.7071067811865476,
|
||||||
'SQRT2': 1.4142135623730951
|
'SQRT2': 1.4142135623730951
|
||||||
}
|
}
|
||||||
|
def is_infinity(x):
|
||||||
|
return x - 1e10 == x
|
||||||
|
|
||||||
class MathFunctions:
|
class MathFunctions:
|
||||||
def abs(this, args):
|
def abs(this, args):
|
||||||
@@ -65,22 +66,22 @@ class MathFunctions:
|
|||||||
def ceil(this, args):
|
def ceil(this, args):
|
||||||
x = get_arg(args, 0)
|
x = get_arg(args, 0)
|
||||||
a = to_number(x)
|
a = to_number(x)
|
||||||
if a != a: # it must be a nan
|
if not is_finite(x):
|
||||||
return NaN
|
return x
|
||||||
return float(math.ceil(a))
|
return float(math.ceil(a))
|
||||||
|
|
||||||
def floor(this, args):
|
def floor(this, args):
|
||||||
x = get_arg(args, 0)
|
x = get_arg(args, 0)
|
||||||
a = to_number(x)
|
a = to_number(x)
|
||||||
if a != a: # it must be a nan
|
if not is_finite(x):
|
||||||
return NaN
|
return x
|
||||||
return float(math.floor(a))
|
return float(math.floor(a))
|
||||||
|
|
||||||
def round(this, args):
|
def round(this, args):
|
||||||
x = get_arg(args, 0)
|
x = get_arg(args, 0)
|
||||||
a = to_number(x)
|
a = to_number(x)
|
||||||
if a != a: # it must be a nan
|
if not is_finite(x):
|
||||||
return NaN
|
return x
|
||||||
return float(round(a))
|
return float(round(a))
|
||||||
|
|
||||||
def sin(this, args):
|
def sin(this, args):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from ..conversions import *
|
from ..conversions import *
|
||||||
from ..func_utils import *
|
from ..func_utils import *
|
||||||
|
from six import unichr
|
||||||
|
|
||||||
def fromCharCode(this, args):
|
def fromCharCode(this, args):
|
||||||
res = u''
|
res = u''
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ def DaylightSavingTA(t):
|
|||||||
return t
|
return t
|
||||||
try:
|
try:
|
||||||
return int(
|
return int(
|
||||||
LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(
|
LOCAL_ZONE.dst(datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||||
t // 1000)).seconds) * 1000
|
seconds=t // 1000)).seconds) * 1000
|
||||||
except:
|
except:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Invalid datetime date, assumed DST time, may be inaccurate...',
|
'Invalid datetime date, assumed DST time, may be inaccurate...',
|
||||||
|
|||||||
@@ -798,7 +798,7 @@ OP_CODES = {}
|
|||||||
g = ''
|
g = ''
|
||||||
for g in globals():
|
for g in globals():
|
||||||
try:
|
try:
|
||||||
if not issubclass(globals()[g], OP_CODE) or g is 'OP_CODE':
|
if not issubclass(globals()[g], OP_CODE) or g == 'OP_CODE':
|
||||||
continue
|
continue
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ def replacement_template(rep, source, span, npar):
|
|||||||
res += '$'
|
res += '$'
|
||||||
n += 2
|
n += 2
|
||||||
continue
|
continue
|
||||||
|
elif rep[n + 1] == '&':
|
||||||
|
# replace with matched string
|
||||||
|
res += source[span[0]:span[1]]
|
||||||
|
n += 2
|
||||||
|
continue
|
||||||
elif rep[n + 1] == '`':
|
elif rep[n + 1] == '`':
|
||||||
# replace with string that is BEFORE match
|
# replace with string that is BEFORE match
|
||||||
res += source[:span[0]]
|
res += source[:span[0]]
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from timeit import timeit
|
from timeit import timeit
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from array import array
|
from array import array
|
||||||
from itertools import izip
|
try:
|
||||||
|
#python 2 code
|
||||||
|
from itertools import izip as zip
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +54,7 @@ t = []
|
|||||||
|
|
||||||
Type = None
|
Type = None
|
||||||
try:
|
try:
|
||||||
print timeit(
|
print(timeit(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
t.append(4)
|
t.append(4)
|
||||||
@@ -56,7 +63,7 @@ t.pop()
|
|||||||
|
|
||||||
|
|
||||||
""",
|
""",
|
||||||
"from __main__ import X,Y,namedtuple,array,t,add,Type, izip",
|
"from __main__ import X,Y,namedtuple,array,t,add,Type, zip",
|
||||||
number=1000000)
|
number=1000000))
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from __future__ import print_function
|
||||||
from string import ascii_lowercase, digits
|
from string import ascii_lowercase, digits
|
||||||
##################################
|
##################################
|
||||||
StringName = u'PyJsConstantString%d_'
|
StringName = u'PyJsConstantString%d_'
|
||||||
@@ -305,4 +306,4 @@ if __name__ == '__main__':
|
|||||||
''')
|
''')
|
||||||
|
|
||||||
t, d = remove_constants(test)
|
t, d = remove_constants(test)
|
||||||
print t, d
|
print(t, d)
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ If case of parsing errors it must return a pos of error.
|
|||||||
NOTES:
|
NOTES:
|
||||||
Strings and other literals are not present so each = means assignment
|
Strings and other literals are not present so each = means assignment
|
||||||
"""
|
"""
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from utils import *
|
from utils import *
|
||||||
from jsparser import *
|
from jsparser import *
|
||||||
|
|
||||||
@@ -80,4 +82,4 @@ def bass_translator(s):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print bass_translator('3.ddsd = 40')
|
print(bass_translator('3.ddsd = 40'))
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ FOR 123
|
|||||||
FOR iter
|
FOR iter
|
||||||
CONTINUE, BREAK, RETURN, LABEL, THROW, TRY, SWITCH
|
CONTINUE, BREAK, RETURN, LABEL, THROW, TRY, SWITCH
|
||||||
"""
|
"""
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from utils import *
|
from utils import *
|
||||||
from jsparser import *
|
from jsparser import *
|
||||||
from nodevisitor import exp_translator
|
from nodevisitor import exp_translator
|
||||||
@@ -477,4 +479,4 @@ def translate_flow(source):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
#print do_dowhile('do {} while(k+f)', 0)[0]
|
#print do_dowhile('do {} while(k+f)', 0)[0]
|
||||||
#print 'e: "%s"'%do_expression('++(c?g:h); mj', 0)[0]
|
#print 'e: "%s"'%do_expression('++(c?g:h); mj', 0)[0]
|
||||||
print translate_flow('a; yimport test')[0]
|
print(translate_flow('a; yimport test')[0])
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
"""This module removes JS functions from source code"""
|
"""This module removes JS functions from source code"""
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from jsparser import *
|
from jsparser import *
|
||||||
from utils import *
|
from utils import *
|
||||||
|
|
||||||
@@ -94,5 +96,5 @@ def remove_functions(source, all_inline=False):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print remove_functions(
|
print(remove_functions(
|
||||||
'5+5 function n (functiona ,functionaj) {dsd s, dsdd}')
|
'5+5 function n (functiona ,functionaj) {dsd s, dsdd}'))
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ TODO
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from utils import *
|
from utils import *
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ OP_METHODS = {
|
|||||||
|
|
||||||
def dbg(source):
|
def dbg(source):
|
||||||
try:
|
try:
|
||||||
with open('C:\Users\Piotrek\Desktop\dbg.py', 'w') as f:
|
with open(r'C:\Users\Piotrek\Desktop\dbg.py', 'w') as f:
|
||||||
f.write(source)
|
f.write(source)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@@ -77,13 +78,13 @@ def indent(lines, ind=4):
|
|||||||
def inject_before_lval(source, lval, code):
|
def inject_before_lval(source, lval, code):
|
||||||
if source.count(lval) > 1:
|
if source.count(lval) > 1:
|
||||||
dbg(source)
|
dbg(source)
|
||||||
print
|
print()
|
||||||
print lval
|
print(lval)
|
||||||
raise RuntimeError('To many lvals (%s)' % lval)
|
raise RuntimeError('To many lvals (%s)' % lval)
|
||||||
elif not source.count(lval):
|
elif not source.count(lval):
|
||||||
dbg(source)
|
dbg(source)
|
||||||
print
|
print()
|
||||||
print lval
|
print(lval)
|
||||||
assert lval not in source
|
assert lval not in source
|
||||||
raise RuntimeError('No lval found "%s"' % lval)
|
raise RuntimeError('No lval found "%s"' % lval)
|
||||||
end = source.index(lval)
|
end = source.index(lval)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from jsparser import *
|
from jsparser import *
|
||||||
from utils import *
|
from utils import *
|
||||||
import re
|
import re
|
||||||
@@ -557,6 +559,6 @@ if __name__ == '__main__':
|
|||||||
#print 'Here', trans('(eee ) . ii [ PyJsMarker ] [ jkj ] ( j , j ) .
|
#print 'Here', trans('(eee ) . ii [ PyJsMarker ] [ jkj ] ( j , j ) .
|
||||||
# jiji (h , ji , i)(non )( )()()()')
|
# jiji (h , ji , i)(non )( )()()()')
|
||||||
for e in xrange(3):
|
for e in xrange(3):
|
||||||
print exp_translator('jk = kk.ik++')
|
print(exp_translator('jk = kk.ik++'))
|
||||||
#First line translated with PyJs: PyJsStrictEq(PyJsAdd((Js(100)*Js(50)),Js(30)), Js("5030")), yay!
|
#First line translated with PyJs: PyJsStrictEq(PyJsAdd((Js(100)*Js(50)),Js(30)), Js("5030")), yay!
|
||||||
print exp_translator('delete a.f')
|
print(exp_translator('delete a.f'))
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
""" This module removes all objects/arrays from JS source code and replace them with LVALS.
|
""" This module removes all objects/arrays from JS source code and replace them with LVALS.
|
||||||
Also it has s function translating removed object/array to python code.
|
Also it has s function translating removed object/array to python code.
|
||||||
Use this module just after removing constants. Later move on to removing functions"""
|
Use this module just after removing constants. Later move on to removing functions"""
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
OBJECT_LVAL = 'PyJsLvalObject%d_'
|
OBJECT_LVAL = 'PyJsLvalObject%d_'
|
||||||
ARRAY_LVAL = 'PyJsLvalArray%d_'
|
ARRAY_LVAL = 'PyJsLvalArray%d_'
|
||||||
from utils import *
|
from utils import *
|
||||||
@@ -180,7 +182,7 @@ def translate_object(obj, lval, obj_count=1, arr_count=1):
|
|||||||
try:
|
try:
|
||||||
key, value = spl
|
key, value = spl
|
||||||
except: #len(spl)> 2
|
except: #len(spl)> 2
|
||||||
print 'Unusual case ' + repr(e)
|
print('Unusual case ' + repr(e))
|
||||||
key = spl[0]
|
key = spl[0]
|
||||||
value = ':'.join(spl[1:])
|
value = ':'.join(spl[1:])
|
||||||
key = key.strip()
|
key = key.strip()
|
||||||
@@ -293,8 +295,8 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
#print remove_objects(test)
|
#print remove_objects(test)
|
||||||
#print list(bracket_split(' {}'))
|
#print list(bracket_split(' {}'))
|
||||||
print
|
print()
|
||||||
print remove_arrays(
|
print(remove_arrays(
|
||||||
'typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""], [][[5][5]])[1].toLowerCase()])'
|
'typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""], [][[5][5]])[1].toLowerCase()])'
|
||||||
)
|
))
|
||||||
print is_object('', ')')
|
print(is_object('', ')'))
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from flow import translate_flow
|
from flow import translate_flow
|
||||||
from constants import remove_constants, recover_constants
|
from constants import remove_constants, recover_constants
|
||||||
from objects import remove_objects, remove_arrays, translate_object, translate_array, set_func_translator
|
from objects import remove_objects, remove_arrays, translate_object, translate_array, set_func_translator
|
||||||
@@ -148,4 +150,4 @@ if __name__ == '__main__':
|
|||||||
#res = translate_js(jq)
|
#res = translate_js(jq)
|
||||||
res = translate_js(t)
|
res = translate_js(t)
|
||||||
dbg(SANDBOX % indent(res))
|
dbg(SANDBOX % indent(res))
|
||||||
print 'Done'
|
print('Done')
|
||||||
|
|||||||
+33
-11
@@ -1,10 +1,16 @@
|
|||||||
__all__ = ['require']
|
__all__ = ['require']
|
||||||
|
|
||||||
import subprocess, os, codecs, glob
|
import subprocess, os, codecs, glob
|
||||||
from .evaljs import translate_js, DEFAULT_HEADER
|
from .evaljs import translate_js, DEFAULT_HEADER
|
||||||
|
from .translators.friendly_nodes import is_valid_py_name
|
||||||
import six
|
import six
|
||||||
|
import tempfile
|
||||||
|
import hashlib
|
||||||
|
import random
|
||||||
|
|
||||||
DID_INIT = False
|
DID_INIT = False
|
||||||
DIRNAME = os.path.dirname(os.path.abspath(__file__))
|
DIRNAME = tempfile.mkdtemp()
|
||||||
PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'py_node_modules')
|
PY_NODE_MODULES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'py_node_modules')
|
||||||
|
|
||||||
|
|
||||||
def _init():
|
def _init():
|
||||||
@@ -46,23 +52,33 @@ GET_FROM_GLOBALS_FUNC = '''
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
def _get_module_py_name(module_name):
|
def _get_module_py_name(module_name):
|
||||||
return module_name.replace('-', '_')
|
return module_name.replace('-', '_')
|
||||||
|
|
||||||
|
|
||||||
def _get_module_var_name(module_name):
|
def _get_module_var_name(module_name):
|
||||||
return _get_module_py_name(module_name).rpartition('/')[-1]
|
cand = _get_module_py_name(module_name).rpartition('/')[-1]
|
||||||
|
if not is_valid_py_name(cand):
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid Python module name %s (generated from %s). Unsupported/invalid npm module specification?" % (
|
||||||
|
repr(cand), repr(module_name)))
|
||||||
|
return cand
|
||||||
|
|
||||||
|
|
||||||
def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False):
|
def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False, maybe_version_str=""):
|
||||||
assert isinstance(module_name, str), 'module_name must be a string!'
|
assert isinstance(module_name, str), 'module_name must be a string!'
|
||||||
|
|
||||||
py_name = _get_module_py_name(module_name)
|
py_name = _get_module_py_name(module_name)
|
||||||
module_filename = '%s.py' % py_name
|
module_filename = '%s.py' % py_name
|
||||||
var_name = _get_module_var_name(module_name)
|
var_name = _get_module_var_name(module_name)
|
||||||
if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH,
|
if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH,
|
||||||
module_filename)) or update:
|
module_filename)) or update:
|
||||||
_init()
|
_init()
|
||||||
in_file_name = 'tmp0in439341018923js2py.js'
|
module_hash = hashlib.sha1(module_name.encode("utf-8")).hexdigest()[:15]
|
||||||
out_file_name = 'tmp0out439341018923js2py.js'
|
version = random.randrange(10000000000000)
|
||||||
|
in_file_name = 'in_%s_%d.js' % (module_hash, version)
|
||||||
|
out_file_name = 'out_%s_%d.js' % (module_hash, version)
|
||||||
code = ADD_TO_GLOBALS_FUNC
|
code = ADD_TO_GLOBALS_FUNC
|
||||||
if include_polyfill:
|
if include_polyfill:
|
||||||
code += "\n;require('babel-polyfill');\n"
|
code += "\n;require('babel-polyfill');\n"
|
||||||
@@ -74,6 +90,8 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa
|
|||||||
f.write(code.encode('utf-8') if six.PY3 else code)
|
f.write(code.encode('utf-8') if six.PY3 else code)
|
||||||
|
|
||||||
pkg_name = module_name.partition('/')[0]
|
pkg_name = module_name.partition('/')[0]
|
||||||
|
if maybe_version_str:
|
||||||
|
pkg_name += '@' + maybe_version_str
|
||||||
# make sure the module is installed
|
# make sure the module is installed
|
||||||
assert subprocess.call(
|
assert subprocess.call(
|
||||||
'cd %s;npm install %s' % (repr(DIRNAME), pkg_name),
|
'cd %s;npm install %s' % (repr(DIRNAME), pkg_name),
|
||||||
@@ -93,7 +111,7 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa
|
|||||||
with codecs.open(os.path.join(DIRNAME, out_file_name), "r",
|
with codecs.open(os.path.join(DIRNAME, out_file_name), "r",
|
||||||
"utf-8") as f:
|
"utf-8") as f:
|
||||||
js_code = f.read()
|
js_code = f.read()
|
||||||
os.remove(os.path.join(DIRNAME, out_file_name))
|
print("Bundled JS library dumped at: %s" % os.path.join(DIRNAME, out_file_name))
|
||||||
if len(js_code) < 50:
|
if len(js_code) < 50:
|
||||||
raise RuntimeError("Candidate JS bundle too short - likely browserify issue.")
|
raise RuntimeError("Candidate JS bundle too short - likely browserify issue.")
|
||||||
js_code += GET_FROM_GLOBALS_FUNC
|
js_code += GET_FROM_GLOBALS_FUNC
|
||||||
@@ -117,21 +135,25 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa
|
|||||||
return py_code
|
return py_code
|
||||||
|
|
||||||
|
|
||||||
def require(module_name, include_polyfill=False, update=False, context=None):
|
def require(module_name, include_polyfill=True, update=False, context=None):
|
||||||
"""
|
"""
|
||||||
Installs the provided npm module, exports a js bundle via browserify, converts to ECMA 5.1 via babel and
|
Installs the provided npm module, exports a js bundle via browserify, converts to ECMA 5.1 via babel and
|
||||||
finally translates the generated JS bundle to Python via Js2Py.
|
finally translates the generated JS bundle to Python via Js2Py.
|
||||||
Returns a pure python object that behaves like the installed module. Nice!
|
Returns a pure python object that behaves like the installed module. Nice!
|
||||||
|
|
||||||
:param module_name: Name of the npm module to require. For example 'esprima'.
|
:param module_name: Name of the npm module to require. For example 'esprima'. Supports specific versions via @
|
||||||
|
specification. Eg: 'crypto-js@3.3'.
|
||||||
:param include_polyfill: Whether the babel-polyfill should be included as part of the translation. May be needed
|
:param include_polyfill: Whether the babel-polyfill should be included as part of the translation. May be needed
|
||||||
for some modules that use unsupported features.
|
for some modules that use unsupported features of JS6 such as Map or typed arrays.
|
||||||
:param update: Whether to force update the translation. Otherwise uses a cached version if exists.
|
:param update: Whether to force update the translation. Otherwise uses a cached version if exists.
|
||||||
:param context: Optional context in which the translated module should be executed in. If provided, the
|
:param context: Optional context in which the translated module should be executed in. If provided, the
|
||||||
header (js2py imports) will be skipped as it is assumed that the context already has all the necessary imports.
|
header (js2py imports) will be skipped as it is assumed that the context already has all the necessary imports.
|
||||||
:return: The JsObjectWrapper containing the translated module object. Can be used like a standard python object.
|
:return: The JsObjectWrapper containing the translated module object. Can be used like a standard python object.
|
||||||
"""
|
"""
|
||||||
py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=update)
|
module_name, maybe_version = (module_name+"@@@").split('@')[:2]
|
||||||
|
|
||||||
|
py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=update,
|
||||||
|
maybe_version_str=maybe_version)
|
||||||
# this is a bit hacky but we need to strip the default header from the generated code...
|
# this is a bit hacky but we need to strip the default header from the generated code...
|
||||||
if context is not None:
|
if context is not None:
|
||||||
if not py_code.startswith(DEFAULT_HEADER):
|
if not py_code.startswith(DEFAULT_HEADER):
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ def replacement_template(rep, source, span, npar):
|
|||||||
res += '$'
|
res += '$'
|
||||||
n += 2
|
n += 2
|
||||||
continue
|
continue
|
||||||
|
elif rep[n + 1] == '&':
|
||||||
|
# replace with matched string
|
||||||
|
res += source[span[0]:span[1]]
|
||||||
|
n += 2
|
||||||
|
continue
|
||||||
elif rep[n + 1] == '`':
|
elif rep[n + 1] == '`':
|
||||||
# replace with string that is BEFORE match
|
# replace with string that is BEFORE match
|
||||||
res += source[:span[0]]
|
res += source[:span[0]]
|
||||||
|
|||||||
@@ -14,26 +14,36 @@ if six.PY3:
|
|||||||
LINE_LEN_LIMIT = 400 # 200 # or any other value - the larger the smaller probability of errors :)
|
LINE_LEN_LIMIT = 400 # 200 # or any other value - the larger the smaller probability of errors :)
|
||||||
|
|
||||||
|
|
||||||
class ForController:
|
class LoopController:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.inside = [False]
|
self.update = [""]
|
||||||
self.update = ''
|
self.label_to_update_idx = {}
|
||||||
|
|
||||||
def enter_for(self, update):
|
def enter(self, update=""):
|
||||||
self.inside.append(True)
|
self.update.append(update)
|
||||||
self.update = update
|
|
||||||
|
|
||||||
def leave_for(self):
|
def leave(self):
|
||||||
self.inside.pop()
|
self.update.pop()
|
||||||
|
|
||||||
|
def get_update(self, label=None):
|
||||||
|
if label is None:
|
||||||
|
return self.update[-1]
|
||||||
|
if label not in self.label_to_update_idx:
|
||||||
|
raise SyntaxError("Undefined label %s" % label)
|
||||||
|
if self.label_to_update_idx[label] >= len(self.update):
|
||||||
|
raise SyntaxError("%s is not a iteration statement label?" % label)
|
||||||
|
return self.update[self.label_to_update_idx[label]]
|
||||||
|
|
||||||
|
def register_label(self, label):
|
||||||
|
if label in self.label_to_update_idx:
|
||||||
|
raise SyntaxError("label %s already used")
|
||||||
|
self.label_to_update_idx[label] = len(self.update)
|
||||||
|
|
||||||
|
def deregister_label(self, label):
|
||||||
|
del self.label_to_update_idx[label]
|
||||||
|
|
||||||
def enter_other(self):
|
|
||||||
self.inside.append(False)
|
|
||||||
|
|
||||||
def leave_other(self):
|
|
||||||
self.inside.pop()
|
|
||||||
|
|
||||||
def is_inside(self):
|
|
||||||
return self.inside[-1]
|
|
||||||
|
|
||||||
|
|
||||||
class InlineStack:
|
class InlineStack:
|
||||||
@@ -86,9 +96,10 @@ class ContextStack:
|
|||||||
|
|
||||||
|
|
||||||
def clean_stacks():
|
def clean_stacks():
|
||||||
global Context, inline_stack
|
global Context, inline_stack, loop_controller
|
||||||
Context = ContextStack()
|
Context = ContextStack()
|
||||||
inline_stack = InlineStack()
|
inline_stack = InlineStack()
|
||||||
|
loop_controller = LoopController()
|
||||||
|
|
||||||
|
|
||||||
def to_key(literal_or_identifier):
|
def to_key(literal_or_identifier):
|
||||||
@@ -108,6 +119,13 @@ def to_key(literal_or_identifier):
|
|||||||
else:
|
else:
|
||||||
return unicode(k)
|
return unicode(k)
|
||||||
|
|
||||||
|
def is_iteration_statement(cand):
|
||||||
|
if not isinstance(cand, dict):
|
||||||
|
# Multiple statements.
|
||||||
|
return False
|
||||||
|
return cand.get("type", "?") in {"ForStatement", "ForInStatement", "WhileStatement", "DoWhileStatement"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def trans(ele, standard=False):
|
def trans(ele, standard=False):
|
||||||
"""Translates esprima syntax tree to python by delegating to appropriate translating node"""
|
"""Translates esprima syntax tree to python by delegating to appropriate translating node"""
|
||||||
@@ -367,9 +385,14 @@ def BreakStatement(type, label):
|
|||||||
|
|
||||||
def ContinueStatement(type, label):
|
def ContinueStatement(type, label):
|
||||||
if label:
|
if label:
|
||||||
return 'raise %s("Continued")\n' % (get_continue_label(label['name']))
|
maybe_update_expr = loop_controller.get_update(label=label['name'])
|
||||||
|
continue_stmt = 'raise %s("Continued")\n' % (get_continue_label(label['name']))
|
||||||
else:
|
else:
|
||||||
return 'continue\n'
|
maybe_update_expr = loop_controller.get_update()
|
||||||
|
continue_stmt = "continue\n"
|
||||||
|
if maybe_update_expr:
|
||||||
|
return "# continue update\n%s\n%s" % (maybe_update_expr, continue_stmt)
|
||||||
|
return continue_stmt
|
||||||
|
|
||||||
|
|
||||||
def ReturnStatement(type, argument):
|
def ReturnStatement(type, argument):
|
||||||
@@ -386,24 +409,28 @@ def DebuggerStatement(type):
|
|||||||
|
|
||||||
|
|
||||||
def DoWhileStatement(type, body, test):
|
def DoWhileStatement(type, body, test):
|
||||||
inside = trans(body) + 'if not %s:\n' % trans(test) + indent('break\n')
|
loop_controller.enter()
|
||||||
|
body_code = trans(body)
|
||||||
|
loop_controller.leave()
|
||||||
|
inside = body_code + 'if not %s:\n' % trans(test) + indent('break\n')
|
||||||
result = 'while 1:\n' + indent(inside)
|
result = 'while 1:\n' + indent(inside)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def ForStatement(type, init, test, update, body):
|
def ForStatement(type, init, test, update, body):
|
||||||
update = indent(trans(update)) if update else ''
|
update = trans(update) if update else ''
|
||||||
init = trans(init) if init else ''
|
init = trans(init) if init else ''
|
||||||
if not init.endswith('\n'):
|
if not init.endswith('\n'):
|
||||||
init += '\n'
|
init += '\n'
|
||||||
test = trans(test) if test else '1'
|
test = trans(test) if test else '1'
|
||||||
|
loop_controller.enter(update)
|
||||||
if not update:
|
if not update:
|
||||||
result = '#for JS loop\n%swhile %s:\n%s%s\n' % (
|
result = '#for JS loop\n%swhile %s:\n%s%s\n' % (
|
||||||
init, test, indent(trans(body)), update)
|
init, test, indent(trans(body)), update)
|
||||||
else:
|
else:
|
||||||
result = '#for JS loop\n%swhile %s:\n' % (init, test)
|
result = '#for JS loop\n%swhile %s:\n' % (init, test)
|
||||||
body = 'try:\n%sfinally:\n %s\n' % (indent(trans(body)), update)
|
result += indent("%s# update\n%s\n" % (trans(body), update))
|
||||||
result += indent(body)
|
loop_controller.leave()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -422,7 +449,9 @@ def ForInStatement(type, left, right, body, each):
|
|||||||
name = left['name']
|
name = left['name']
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Unusual ForIn loop')
|
raise RuntimeError('Unusual ForIn loop')
|
||||||
|
loop_controller.enter()
|
||||||
res += indent('var.put(%s, PyJsTemp)\n' % repr(name) + trans(body))
|
res += indent('var.put(%s, PyJsTemp)\n' % repr(name) + trans(body))
|
||||||
|
loop_controller.leave()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
@@ -438,20 +467,23 @@ def IfStatement(type, test, consequent, alternate):
|
|||||||
|
|
||||||
def LabeledStatement(type, label, body):
|
def LabeledStatement(type, label, body):
|
||||||
# todo consider using smarter approach!
|
# todo consider using smarter approach!
|
||||||
|
label_name = label['name']
|
||||||
|
loop_controller.register_label(label_name)
|
||||||
inside = trans(body)
|
inside = trans(body)
|
||||||
|
loop_controller.deregister_label(label_name)
|
||||||
defs = ''
|
defs = ''
|
||||||
if inside.startswith('while ') or inside.startswith(
|
if is_iteration_statement(body) and (inside.startswith('while ') or inside.startswith(
|
||||||
'for ') or inside.startswith('#for'):
|
'for ') or inside.startswith('#for')):
|
||||||
# we have to add contine label as well...
|
# we have to add contine label as well...
|
||||||
# 3 or 1 since #for loop type has more lines before real for.
|
# 3 or 1 since #for loop type has more lines before real for.
|
||||||
sep = 1 if not inside.startswith('#for') else 3
|
sep = 1 if not inside.startswith('#for') else 3
|
||||||
cont_label = get_continue_label(label['name'])
|
cont_label = get_continue_label(label_name)
|
||||||
temp = inside.split('\n')
|
temp = inside.split('\n')
|
||||||
injected = 'try:\n' + '\n'.join(temp[sep:])
|
injected = 'try:\n' + '\n'.join(temp[sep:])
|
||||||
injected += 'except %s:\n pass\n' % cont_label
|
injected += 'except %s:\n pass\n' % cont_label
|
||||||
inside = '\n'.join(temp[:sep]) + '\n' + indent(injected)
|
inside = '\n'.join(temp[:sep]) + '\n' + indent(injected)
|
||||||
defs += 'class %s(Exception): pass\n' % cont_label
|
defs += 'class %s(Exception): pass\n' % cont_label
|
||||||
break_label = get_break_label(label['name'])
|
break_label = get_break_label(label_name)
|
||||||
inside = 'try:\n%sexcept %s:\n pass\n' % (indent(inside), break_label)
|
inside = 'try:\n%sexcept %s:\n pass\n' % (indent(inside), break_label)
|
||||||
defs += 'class %s(Exception): pass\n' % break_label
|
defs += 'class %s(Exception): pass\n' % break_label
|
||||||
return defs + inside
|
return defs + inside
|
||||||
@@ -546,7 +578,11 @@ def VariableDeclaration(type, declarations, kind):
|
|||||||
|
|
||||||
|
|
||||||
def WhileStatement(type, test, body):
|
def WhileStatement(type, test, body):
|
||||||
result = 'while %s:\n' % trans(test) + indent(trans(body))
|
test_code = trans(test)
|
||||||
|
loop_controller.enter()
|
||||||
|
body_code = trans(body)
|
||||||
|
loop_controller.leave()
|
||||||
|
result = 'while %s:\n' % test_code + indent(body_code)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -55,16 +55,19 @@ def dbg(x):
|
|||||||
"""does nothing, legacy dummy function"""
|
"""does nothing, legacy dummy function"""
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
# Another way of doing that would be with my auto esprima translation but its much slower:
|
||||||
|
# parsed = esprima.parse(js).to_dict()
|
||||||
|
def pyjsparser_parse_fn(code):
|
||||||
|
parser = pyjsparser.PyJsParser()
|
||||||
|
return parser.parse(code)
|
||||||
|
|
||||||
def translate_js(js, HEADER=DEFAULT_HEADER, use_compilation_plan=False):
|
def translate_js(js, HEADER=DEFAULT_HEADER, use_compilation_plan=False, parse_fn=pyjsparser_parse_fn):
|
||||||
"""js has to be a javascript source code.
|
"""js has to be a javascript source code.
|
||||||
returns equivalent python code."""
|
returns equivalent python code."""
|
||||||
if use_compilation_plan and not '//' in js and not '/*' in js:
|
if use_compilation_plan and not '//' in js and not '/*' in js:
|
||||||
return translate_js_with_compilation_plan(js, HEADER=HEADER)
|
return translate_js_with_compilation_plan(js, HEADER=HEADER)
|
||||||
parser = pyjsparser.PyJsParser()
|
|
||||||
parsed = parser.parse(js) # js to esprima syntax tree
|
parsed = parse_fn(js)
|
||||||
# Another way of doing that would be with my auto esprima translation but its much slower and causes import problems:
|
|
||||||
# parsed = esprima.parse(js).to_dict()
|
|
||||||
translating_nodes.clean_stacks()
|
translating_nodes.clean_stacks()
|
||||||
return HEADER + translating_nodes.trans(
|
return HEADER + translating_nodes.trans(
|
||||||
parsed) # syntax tree to python code
|
parsed) # syntax tree to python code
|
||||||
|
|||||||
+135
-29
@@ -26,17 +26,19 @@ def fix_js_args(func):
|
|||||||
return func
|
return func
|
||||||
code = append_arguments(six.get_function_code(func), ('this', 'arguments'))
|
code = append_arguments(six.get_function_code(func), ('this', 'arguments'))
|
||||||
|
|
||||||
return types.FunctionType(
|
result = types.FunctionType(
|
||||||
code,
|
code,
|
||||||
six.get_function_globals(func),
|
six.get_function_globals(func),
|
||||||
func.__name__,
|
func.__name__,
|
||||||
closure=six.get_function_closure(func))
|
closure=six.get_function_closure(func))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def append_arguments(code_obj, new_locals):
|
def append_arguments(code_obj, new_locals):
|
||||||
co_varnames = code_obj.co_varnames # Old locals
|
co_varnames = code_obj.co_varnames # Old locals
|
||||||
co_names = code_obj.co_names # Old globals
|
co_names = code_obj.co_names # Old globals
|
||||||
co_names += tuple(e for e in new_locals if e not in co_names)
|
new_args = tuple(e for e in new_locals if e not in co_names)
|
||||||
|
co_names += new_args
|
||||||
co_argcount = code_obj.co_argcount # Argument count
|
co_argcount = code_obj.co_argcount # Argument count
|
||||||
co_code = code_obj.co_code # The actual bytecode as a string
|
co_code = code_obj.co_code # The actual bytecode as a string
|
||||||
|
|
||||||
@@ -76,26 +78,51 @@ def append_arguments(code_obj, new_locals):
|
|||||||
names_to_varnames = dict(
|
names_to_varnames = dict(
|
||||||
(co_names.index(name), varnames.index(name)) for name in new_locals)
|
(co_names.index(name), varnames.index(name)) for name in new_locals)
|
||||||
|
|
||||||
|
is_new_bytecode = sys.version_info >= (3, 11)
|
||||||
# Now we modify the actual bytecode
|
# Now we modify the actual bytecode
|
||||||
modified = []
|
modified = []
|
||||||
|
drop_future_cache = False
|
||||||
for inst in instructions(code_obj):
|
for inst in instructions(code_obj):
|
||||||
|
if is_new_bytecode and inst.opname == "CACHE":
|
||||||
|
assert inst.arg == 0
|
||||||
|
if not drop_future_cache:
|
||||||
|
modified.extend(write_instruction(inst.opcode, inst.arg))
|
||||||
|
else:
|
||||||
|
# We need to inject NOOP to not break jumps :(
|
||||||
|
modified.extend(write_instruction(dis.opmap["NOP"], 0))
|
||||||
|
|
||||||
|
continue
|
||||||
op, arg = inst.opcode, inst.arg
|
op, arg = inst.opcode, inst.arg
|
||||||
# If the instruction is a LOAD_GLOBAL, we have to check to see if
|
# If the instruction is a LOAD_GLOBAL, we have to check to see if
|
||||||
# it's one of the globals that we are replacing. Either way,
|
# it's one of the globals that we are replacing. Either way,
|
||||||
# update its arg using the appropriate dict.
|
# update its arg using the appropriate dict.
|
||||||
|
drop_future_cache = False
|
||||||
if inst.opcode == LOAD_GLOBAL:
|
if inst.opcode == LOAD_GLOBAL:
|
||||||
if inst.arg in names_to_varnames:
|
idx = inst.arg
|
||||||
|
if is_new_bytecode:
|
||||||
|
idx = idx // 2
|
||||||
|
if idx in names_to_varnames:
|
||||||
op = LOAD_FAST
|
op = LOAD_FAST
|
||||||
arg = names_to_varnames[inst.arg]
|
arg = names_to_varnames[idx]
|
||||||
elif inst.arg in name_translations:
|
# Cache is not present after LOAD_FAST and needs to be removed.
|
||||||
arg = name_translations[inst.arg]
|
drop_future_cache = True
|
||||||
|
elif idx in name_translations:
|
||||||
|
tgt = name_translations[idx]
|
||||||
|
if is_new_bytecode:
|
||||||
|
tgt = 2*tgt + (inst.arg % 2)
|
||||||
|
arg = tgt
|
||||||
else:
|
else:
|
||||||
raise ValueError("a name was lost in translation")
|
raise(ValueError("a name was lost in translation last instruction %s" % str(inst)))
|
||||||
# If it accesses co_varnames or co_names then update its argument.
|
# If it accesses co_varnames or co_names then update its argument.
|
||||||
elif inst.opcode in opcode.haslocal:
|
elif inst.opcode in opcode.haslocal:
|
||||||
arg = varname_translations[inst.arg]
|
arg = varname_translations[inst.arg]
|
||||||
elif inst.opcode in opcode.hasname:
|
elif inst.opcode in opcode.hasname:
|
||||||
|
# for example STORE_GLOBAL
|
||||||
arg = name_translations[inst.arg]
|
arg = name_translations[inst.arg]
|
||||||
|
elif is_new_bytecode and inst.opcode in opcode.hasfree:
|
||||||
|
# Python 3.11+ adds refs at the end (after locals), for whatever reason...
|
||||||
|
if inst.argval not in code_obj.co_varnames[:code_obj.co_argcount]: # we do not need to remap existing arguments, they are not shifted by new ones.
|
||||||
|
arg = inst.arg + len(new_locals)
|
||||||
modified.extend(write_instruction(op, arg))
|
modified.extend(write_instruction(op, arg))
|
||||||
if six.PY2:
|
if six.PY2:
|
||||||
code = ''.join(modified)
|
code = ''.join(modified)
|
||||||
@@ -113,23 +140,26 @@ def append_arguments(code_obj, new_locals):
|
|||||||
code_obj.co_filename, code_obj.co_name,
|
code_obj.co_filename, code_obj.co_name,
|
||||||
code_obj.co_firstlineno, code_obj.co_lnotab,
|
code_obj.co_firstlineno, code_obj.co_lnotab,
|
||||||
code_obj.co_freevars, code_obj.co_cellvars)
|
code_obj.co_freevars, code_obj.co_cellvars)
|
||||||
|
|
||||||
# Done modifying codestring - make the code object
|
# Done modifying codestring - make the code object
|
||||||
if hasattr(code_obj, "replace"):
|
if hasattr(code_obj, "replace"):
|
||||||
# Python 3.8+
|
# Python 3.8+
|
||||||
return code_obj.replace(
|
code_obj = code_obj.replace(
|
||||||
co_argcount=co_argcount + new_locals_len,
|
co_argcount=co_argcount + new_locals_len,
|
||||||
co_nlocals=code_obj.co_nlocals + new_locals_len,
|
co_nlocals=code_obj.co_nlocals + new_locals_len,
|
||||||
co_code=code,
|
co_code=code,
|
||||||
co_names=names,
|
co_names=names,
|
||||||
co_varnames=varnames)
|
co_varnames=varnames)
|
||||||
|
return code_obj
|
||||||
else:
|
else:
|
||||||
return types.CodeType(*args)
|
return types.CodeType(*args)
|
||||||
|
|
||||||
|
|
||||||
def instructions(code_obj):
|
def instructions(code_obj, show_cache=True):
|
||||||
# easy for python 3.4+
|
if sys.version_info >= (3, 11):
|
||||||
if sys.version_info >= (3, 4):
|
# Python 3.11 introduced "cache instructions", hidden by default.
|
||||||
|
for inst in dis.Bytecode(code_obj, show_caches=show_cache):
|
||||||
|
yield inst
|
||||||
|
elif sys.version_info >= (3, 4): # easy for python 3.4+
|
||||||
for inst in dis.Bytecode(code_obj):
|
for inst in dis.Bytecode(code_obj):
|
||||||
yield inst
|
yield inst
|
||||||
else:
|
else:
|
||||||
@@ -171,7 +201,7 @@ def write_instruction(op, arg):
|
|||||||
chr((arg >> 8) & 255)
|
chr((arg >> 8) & 255)
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid oparg: {0} is too large".format(oparg))
|
raise ValueError("Invalid oparg: {0} is too large".format(arg))
|
||||||
else: # python 3.6+ uses wordcode instead of bytecode and they already supply all the EXTENDEND_ARG ops :)
|
else: # python 3.6+ uses wordcode instead of bytecode and they already supply all the EXTENDEND_ARG ops :)
|
||||||
if arg is None:
|
if arg is None:
|
||||||
return [chr(op), 0]
|
return [chr(op), 0]
|
||||||
@@ -191,6 +221,7 @@ def write_instruction(op, arg):
|
|||||||
# raise ValueError("Invalid oparg: {0} is too large".format(oparg))
|
# raise ValueError("Invalid oparg: {0} is too large".format(oparg))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def check(code_obj):
|
def check(code_obj):
|
||||||
old_bytecode = code_obj.co_code
|
old_bytecode = code_obj.co_code
|
||||||
insts = list(instructions(code_obj))
|
insts = list(instructions(code_obj))
|
||||||
@@ -221,24 +252,99 @@ def check(code_obj):
|
|||||||
'Your python version made changes to the bytecode')
|
'Your python version made changes to the bytecode')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def signature(func):
|
||||||
|
code_obj = six.get_function_code(func)
|
||||||
|
return (code_obj.co_nlocals, code_obj.co_argcount, code_obj.co_nlocals, code_obj.co_stacksize,
|
||||||
|
code_obj.co_flags, code_obj.co_names, code_obj.co_varnames,
|
||||||
|
code_obj.co_filename,
|
||||||
|
code_obj.co_freevars, code_obj.co_cellvars)
|
||||||
|
|
||||||
check(six.get_function_code(check))
|
check(six.get_function_code(check))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def compare_func(fake_func, gt_func):
|
||||||
|
print(signature(fake_func))
|
||||||
|
print(signature(gt_func))
|
||||||
|
assert signature(fake_func) == signature(gt_func)
|
||||||
|
fake_ins = list(instructions(six.get_function_code(fake_func), show_cache=False))
|
||||||
|
real_ins = list(instructions(six.get_function_code(gt_func), show_cache=False))
|
||||||
|
offset = 0
|
||||||
|
pos = 0
|
||||||
|
for e in fake_ins:
|
||||||
|
if e.opname == "NOP":
|
||||||
|
offset += 1 # ignore NOPs that are inserted in place of old cache.
|
||||||
|
else:
|
||||||
|
real = real_ins[pos]
|
||||||
|
fake = e
|
||||||
|
print("POS %d OFFSET: %d FAKE VS REAL" % (pos, offset))
|
||||||
|
print(fake)
|
||||||
|
print(real)
|
||||||
|
assert fake.opcode == real.opcode
|
||||||
|
if fake.opcode in dis.hasjabs or fake.opcode in dis.hasjrel:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
assert fake.arg == real.arg
|
||||||
|
assert fake.argval == real.argval or fake.opname in ["LOAD_CONST"]
|
||||||
|
assert fake.is_jump_target == real.is_jump_target
|
||||||
|
|
||||||
|
pos += 1
|
||||||
|
assert pos == len(real_ins), (pos, len(real_ins))
|
||||||
|
print("DONE, looks good.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
x = 'Wrong'
|
import faulthandler
|
||||||
dick = 3000
|
|
||||||
|
|
||||||
def func(a):
|
faulthandler.enable()
|
||||||
print(x, y, z, a)
|
|
||||||
print(dick)
|
|
||||||
d = (x, )
|
|
||||||
for e in (e for e in x):
|
|
||||||
print(e)
|
|
||||||
return x, y, z
|
|
||||||
|
|
||||||
func2 = types.FunctionType(
|
def func(cmpfn):
|
||||||
append_arguments(six.get_function_code(func), ('x', 'y', 'z')),
|
if not this.Class in ('Array', 'Arguments'):
|
||||||
six.get_function_globals(func),
|
return this.to_object() # do nothing
|
||||||
func.__name__,
|
arr = []
|
||||||
closure=six.get_function_closure(func))
|
for i in xrange(len(this)):
|
||||||
args = (2, 2, 3, 4), 3, 4
|
arr.append(this.get(six.text_type(i)))
|
||||||
assert func2(1, *args) == args
|
|
||||||
|
if not arr:
|
||||||
|
return this
|
||||||
|
if not cmpfn.is_callable():
|
||||||
|
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(len(arr)):
|
||||||
|
this.put(six.text_type(i), arr[i])
|
||||||
|
|
||||||
|
return this
|
||||||
|
|
||||||
|
|
||||||
|
def func_gt(cmpfn, this, arguments):
|
||||||
|
if not this.Class in ('Array', 'Arguments'):
|
||||||
|
return this.to_object() # do nothing
|
||||||
|
arr = []
|
||||||
|
for i in xrange(len(this)):
|
||||||
|
arr.append(this.get(six.text_type(i)))
|
||||||
|
|
||||||
|
if not arr:
|
||||||
|
return this
|
||||||
|
if not cmpfn.is_callable():
|
||||||
|
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(len(arr)):
|
||||||
|
this.put(six.text_type(i), arr[i])
|
||||||
|
|
||||||
|
return this
|
||||||
|
|
||||||
|
|
||||||
|
func2 = fix_js_args(func)
|
||||||
|
compare_func(func2, func_gt)
|
||||||
|
|||||||
Reference in New Issue
Block a user