@@ -0,0 +1,75 @@
|
||||
# The MIT License
|
||||
#
|
||||
# Copyright 2014, 2015 Piotr Dabkowski
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the 'Software'),
|
||||
# to deal in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so, subject
|
||||
# to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all copies or
|
||||
# substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
""" This module allows you to translate and execute Javascript in pure python.
|
||||
Basically its implementation of ECMAScript 5.1 in pure python.
|
||||
|
||||
Use eval_js method to execute javascript code and get resulting python object (builtin if possible).
|
||||
|
||||
EXAMPLE:
|
||||
>>> import js2py
|
||||
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
||||
>>> add(1, 2) + 3
|
||||
6
|
||||
>>> add('1', 2, 3)
|
||||
u'12'
|
||||
>>> add.constructor
|
||||
function Function() { [python code] }
|
||||
|
||||
|
||||
Or use EvalJs to execute many javascript code fragments under same context - you would be able to get any
|
||||
variable from the context!
|
||||
|
||||
>>> js = js2py.EvalJs()
|
||||
>>> js.execute('var a = 10; function f(x) {return x*x};')
|
||||
>>> js.f(9)
|
||||
81
|
||||
>>> js.a
|
||||
10
|
||||
|
||||
Also you can use its console method to play with interactive javascript console.
|
||||
|
||||
|
||||
Use parse_js to parse (syntax tree is just like in esprima.js) and translate_js to trasnlate JavaScript.
|
||||
|
||||
Finally, you can use pyimport statement from inside JS code to import and use python libraries.
|
||||
|
||||
>>> js2py.eval_js('pyimport urllib; urllib.urlopen("https://www.google.com")')
|
||||
|
||||
NOTE: This module is still not fully finished:
|
||||
|
||||
Date and JSON builtin objects are not implemented
|
||||
Array prototype is not fully finished (will be soon)
|
||||
|
||||
Other than that everything should work fine.
|
||||
|
||||
"""
|
||||
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
__all__ = [
|
||||
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'parse_js',
|
||||
'translate_file', 'run_file', 'disable_pyimport', 'eval_js6',
|
||||
'translate_js6', 'PyJsException', 'get_file_contents',
|
||||
'write_file_contents', 'require'
|
||||
]
|
||||
|
||||
from .base import PyJsException
|
||||
from .evaljs import *
|
||||
from .translators import parse as parse_js
|
||||
from .node_import import require
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
# The MIT License
|
||||
#
|
||||
# Copyright 2014, 2015 Piotr Dabkowski
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the 'Software'),
|
||||
# to deal in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so, subject
|
||||
# to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all copies or
|
||||
# substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
""" This module allows you to translate and execute Javascript in pure python.
|
||||
Basically its implementation of ECMAScript 5.1 in pure python.
|
||||
|
||||
Use eval_js method to execute javascript code and get resulting python object (builtin if possible).
|
||||
|
||||
EXAMPLE:
|
||||
>>> import js2py
|
||||
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
||||
>>> add(1, 2) + 3
|
||||
6
|
||||
>>> add('1', 2, 3)
|
||||
u'12'
|
||||
>>> add.constructor
|
||||
function Function() { [python code] }
|
||||
|
||||
|
||||
Or use EvalJs to execute many javascript code fragments under same context - you would be able to get any
|
||||
variable from the context!
|
||||
|
||||
>>> js = js2py.EvalJs()
|
||||
>>> js.execute('var a = 10; function f(x) {return x*x};')
|
||||
>>> js.f(9)
|
||||
81
|
||||
>>> js.a
|
||||
10
|
||||
|
||||
Also you can use its console method to play with interactive javascript console.
|
||||
|
||||
|
||||
Use parse_js to parse (syntax tree is just like in esprima.js) and translate_js to trasnlate JavaScript.
|
||||
|
||||
Finally, you can use pyimport statement from inside JS code to import and use python libraries.
|
||||
|
||||
>>> js2py.eval_js('pyimport urllib; urllib.urlopen("https://www.google.com")')
|
||||
|
||||
NOTE: This module is still not fully finished:
|
||||
|
||||
Date and JSON builtin objects are not implemented
|
||||
Array prototype is not fully finished (will be soon)
|
||||
|
||||
Other than that everything should work fine.
|
||||
|
||||
"""
|
||||
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
__all__ = [
|
||||
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'parse_js',
|
||||
'translate_file', 'run_file', 'disable_pyimport', 'eval_js6',
|
||||
'translate_js6', 'PyJsException', 'get_file_contents',
|
||||
'write_file_contents', 'require'
|
||||
]
|
||||
|
||||
from .base import PyJsException
|
||||
from .evaljs import *
|
||||
from .translators import parse as parse_js
|
||||
from .node_import import require
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,281 +0,0 @@
|
||||
# coding=utf-8
|
||||
from .translators import translate_js, DEFAULT_HEADER
|
||||
from .es6 import js6_to_js5
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import six
|
||||
import os
|
||||
import hashlib
|
||||
import codecs
|
||||
|
||||
__all__ = [
|
||||
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'translate_file',
|
||||
'eval_js6', 'translate_js6', 'run_file', 'disable_pyimport',
|
||||
'get_file_contents', 'write_file_contents'
|
||||
]
|
||||
DEBUG = False
|
||||
|
||||
|
||||
def disable_pyimport():
|
||||
import pyjsparser.parser
|
||||
pyjsparser.parser.ENABLE_PYIMPORT = False
|
||||
|
||||
|
||||
def path_as_local(path):
|
||||
if os.path.isabs(path):
|
||||
return path
|
||||
# relative to cwd
|
||||
return os.path.join(os.getcwd(), path)
|
||||
|
||||
|
||||
def import_js(path, lib_name, globals):
|
||||
"""Imports from javascript source file.
|
||||
globals is your globals()"""
|
||||
with codecs.open(path_as_local(path), "r", "utf-8") as f:
|
||||
js = f.read()
|
||||
e = EvalJs()
|
||||
e.execute(js)
|
||||
var = e.context['var']
|
||||
globals[lib_name] = var.to_python()
|
||||
|
||||
|
||||
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 write_file_contents(path_or_file, contents):
|
||||
if hasattr(path_or_file, 'write'):
|
||||
path_or_file.write(contents)
|
||||
else:
|
||||
with open(path_as_local(path_or_file), 'w') as f:
|
||||
f.write(contents)
|
||||
|
||||
|
||||
def translate_file(input_path, output_path):
|
||||
'''
|
||||
Translates input JS file to python and saves the it to the output path.
|
||||
It appends some convenience code at the end so that it is easy to import JS objects.
|
||||
|
||||
For example we have a file 'example.js' with: var a = function(x) {return x}
|
||||
translate_file('example.js', 'example.py')
|
||||
|
||||
Now example.py can be easily importend and used:
|
||||
>>> from example import example
|
||||
>>> example.a(30)
|
||||
30
|
||||
'''
|
||||
js = get_file_contents(input_path)
|
||||
|
||||
py_code = translate_js(js)
|
||||
lib_name = os.path.basename(output_path).split('.')[0]
|
||||
head = '__all__ = [%s]\n\n# Don\'t look below, you will not understand this Python code :) I don\'t.\n\n' % repr(
|
||||
lib_name)
|
||||
tail = '\n\n# Add lib to the module scope\n%s = var.to_python()' % lib_name
|
||||
out = head + py_code + tail
|
||||
write_file_contents(output_path, out)
|
||||
|
||||
|
||||
def run_file(path_or_file, context=None):
|
||||
''' Context must be EvalJS object. Runs given path as a JS program. Returns (eval_value, context).
|
||||
'''
|
||||
if context is None:
|
||||
context = EvalJs()
|
||||
if not isinstance(context, EvalJs):
|
||||
raise TypeError('context must be the instance of EvalJs')
|
||||
eval_value = context.eval(get_file_contents(path_or_file))
|
||||
return eval_value, context
|
||||
|
||||
|
||||
def eval_js(js):
|
||||
"""Just like javascript eval. Translates javascript to python,
|
||||
executes and returns python object.
|
||||
js is javascript source code
|
||||
|
||||
EXAMPLE:
|
||||
>>> import js2py
|
||||
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
||||
>>> add(1, 2) + 3
|
||||
6
|
||||
>>> add('1', 2, 3)
|
||||
u'12'
|
||||
>>> add.constructor
|
||||
function Function() { [python code] }
|
||||
|
||||
NOTE: For Js Number, String, Boolean and other base types returns appropriate python BUILTIN type.
|
||||
For Js functions and objects, returns Python wrapper - basically behaves like normal python object.
|
||||
If you really want to convert object to python dict you can use to_dict method.
|
||||
"""
|
||||
e = EvalJs()
|
||||
return e.eval(js)
|
||||
|
||||
|
||||
def eval_js6(js):
|
||||
"""Just like eval_js but with experimental support for js6 via babel."""
|
||||
return eval_js(js6_to_js5(js))
|
||||
|
||||
|
||||
def translate_js6(js):
|
||||
"""Just like translate_js but with experimental support for js6 via babel."""
|
||||
return translate_js(js6_to_js5(js))
|
||||
|
||||
|
||||
class EvalJs(object):
|
||||
"""This class supports continuous execution of javascript under same context.
|
||||
|
||||
>>> ctx = EvalJs()
|
||||
>>> ctx.execute('var a = 10;function f(x) {return x*x};')
|
||||
>>> ctx.f(9)
|
||||
81
|
||||
>>> ctx.a
|
||||
10
|
||||
|
||||
context is a python dict or object that contains python variables that should be available to JavaScript
|
||||
For example:
|
||||
>>> ctx = EvalJs({'a': 30})
|
||||
>>> ctx.execute('var x = a')
|
||||
>>> ctx.x
|
||||
30
|
||||
|
||||
You can enable JS require function via enable_require. With this feature enabled you can use js modules
|
||||
from npm, for example:
|
||||
>>> ctx = EvalJs(enable_require=True)
|
||||
>>> ctx.execute("var esprima = require('esprima');")
|
||||
>>> ctx.execute("esprima.parse('var a = 1')")
|
||||
|
||||
You can run interactive javascript console with console method!"""
|
||||
|
||||
def __init__(self, context={}, enable_require=False):
|
||||
self.__dict__['_context'] = {}
|
||||
exec (DEFAULT_HEADER, self._context)
|
||||
self.__dict__['_var'] = self._context['var'].to_python()
|
||||
|
||||
if enable_require:
|
||||
def _js_require_impl(npm_module_name):
|
||||
from .node_import import require
|
||||
from .base import to_python
|
||||
return require(to_python(npm_module_name), context=self._context)
|
||||
setattr(self._var, 'require', _js_require_impl)
|
||||
|
||||
if not isinstance(context, dict):
|
||||
try:
|
||||
context = context.__dict__
|
||||
except:
|
||||
raise TypeError(
|
||||
'context has to be either a dict or have __dict__ attr')
|
||||
for k, v in six.iteritems(context):
|
||||
setattr(self._var, k, v)
|
||||
|
||||
def execute(self, js=None, use_compilation_plan=False):
|
||||
"""executes javascript js in current context
|
||||
|
||||
During initial execute() the converted js is cached for re-use. That means next time you
|
||||
run the same javascript snippet you save many instructions needed to parse and convert the
|
||||
js code to python code.
|
||||
|
||||
This cache causes minor overhead (a cache dicts is updated) but the Js=>Py conversion process
|
||||
is typically expensive compared to actually running the generated python code.
|
||||
|
||||
Note that the cache is just a dict, it has no expiration or cleanup so when running this
|
||||
in automated situations with vast amounts of snippets it might increase memory usage.
|
||||
"""
|
||||
try:
|
||||
cache = self.__dict__['cache']
|
||||
except KeyError:
|
||||
cache = self.__dict__['cache'] = {}
|
||||
hashkey = hashlib.md5(js.encode('utf-8')).digest()
|
||||
try:
|
||||
compiled = cache[hashkey]
|
||||
except KeyError:
|
||||
code = translate_js(
|
||||
js, '', use_compilation_plan=use_compilation_plan)
|
||||
compiled = cache[hashkey] = compile(code, '<EvalJS snippet>',
|
||||
'exec')
|
||||
exec (compiled, self._context)
|
||||
|
||||
def eval(self, expression, use_compilation_plan=False):
|
||||
"""evaluates expression in current context and returns its value"""
|
||||
code = 'PyJsEvalResult = eval(%s)' % json.dumps(expression)
|
||||
self.execute(code, use_compilation_plan=use_compilation_plan)
|
||||
return self['PyJsEvalResult']
|
||||
|
||||
def execute_debug(self, js):
|
||||
"""executes javascript js in current context
|
||||
as opposed to the (faster) self.execute method, you can use your regular debugger
|
||||
to set breakpoints and inspect the generated python code
|
||||
"""
|
||||
code = translate_js(js, '')
|
||||
# make sure you have a temp folder:
|
||||
filename = 'temp' + os.sep + '_' + hashlib.md5(
|
||||
code.encode("utf-8")).hexdigest() + '.py'
|
||||
try:
|
||||
with open(filename, mode='w') as f:
|
||||
f.write(code)
|
||||
with open(filename, "r") as f:
|
||||
pyCode = compile(f.read(), filename, 'exec')
|
||||
exec(pyCode, self._context)
|
||||
|
||||
except Exception as err:
|
||||
raise err
|
||||
finally:
|
||||
os.remove(filename)
|
||||
try:
|
||||
os.remove(filename + 'c')
|
||||
except:
|
||||
pass
|
||||
|
||||
def eval_debug(self, expression):
|
||||
"""evaluates expression in current context and returns its value
|
||||
as opposed to the (faster) self.execute method, you can use your regular debugger
|
||||
to set breakpoints and inspect the generated python code
|
||||
"""
|
||||
code = 'PyJsEvalResult = eval(%s)' % json.dumps(expression)
|
||||
self.execute_debug(code)
|
||||
return self['PyJsEvalResult']
|
||||
|
||||
def __getattr__(self, var):
|
||||
return getattr(self._var, var)
|
||||
|
||||
def __getitem__(self, var):
|
||||
return getattr(self._var, var)
|
||||
|
||||
def __setattr__(self, var, val):
|
||||
return setattr(self._var, var, val)
|
||||
|
||||
def __setitem__(self, var, val):
|
||||
return setattr(self._var, var, val)
|
||||
|
||||
def console(self):
|
||||
"""starts to interact (starts interactive console) Something like code.InteractiveConsole"""
|
||||
while True:
|
||||
if six.PY2:
|
||||
code = raw_input('>>> ')
|
||||
else:
|
||||
code = input('>>>')
|
||||
try:
|
||||
print(self.eval(code))
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except Exception as e:
|
||||
import traceback
|
||||
if DEBUG:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
else:
|
||||
sys.stderr.write('EXCEPTION: ' + str(e) + '\n')
|
||||
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()
|
||||
@@ -1,76 +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
|
||||
@@ -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, ()
|
||||
@@ -1,145 +0,0 @@
|
||||
__all__ = ['require']
|
||||
import subprocess, os, codecs, glob
|
||||
from .evaljs import translate_js, DEFAULT_HEADER
|
||||
import six
|
||||
DID_INIT = False
|
||||
DIRNAME = os.path.dirname(os.path.abspath(__file__))
|
||||
PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'py_node_modules')
|
||||
|
||||
|
||||
def _init():
|
||||
global DID_INIT
|
||||
if DID_INIT:
|
||||
return
|
||||
assert subprocess.call(
|
||||
'node -v', shell=True, cwd=DIRNAME
|
||||
) == 0, 'You must have node installed! run: brew install node'
|
||||
assert subprocess.call(
|
||||
'cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify browserify-shim'
|
||||
% repr(DIRNAME),
|
||||
shell=True,
|
||||
cwd=DIRNAME) == 0, 'Could not link required node_modules'
|
||||
DID_INIT = True
|
||||
|
||||
|
||||
ADD_TO_GLOBALS_FUNC = '''
|
||||
;function addToGlobals(name, obj) {
|
||||
if (!Object.prototype.hasOwnProperty('_fake_exports')) {
|
||||
Object.prototype._fake_exports = {};
|
||||
}
|
||||
Object.prototype._fake_exports[name] = obj;
|
||||
};
|
||||
|
||||
'''
|
||||
# subprocess.call("""node -e 'require("browserify")'""", shell=True)
|
||||
GET_FROM_GLOBALS_FUNC = '''
|
||||
;function getFromGlobals(name) {
|
||||
if (!Object.prototype.hasOwnProperty('_fake_exports')) {
|
||||
throw Error("Could not find any value named "+name);
|
||||
}
|
||||
if (Object.prototype._fake_exports.hasOwnProperty(name)) {
|
||||
return Object.prototype._fake_exports[name];
|
||||
} else {
|
||||
throw Error("Could not find any value named "+name);
|
||||
}
|
||||
};
|
||||
|
||||
'''
|
||||
|
||||
def _get_module_py_name(module_name):
|
||||
return module_name.replace('-', '_')
|
||||
|
||||
def _get_module_var_name(module_name):
|
||||
return _get_module_py_name(module_name).rpartition('/')[-1]
|
||||
|
||||
|
||||
def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False):
|
||||
assert isinstance(module_name, str), 'module_name must be a string!'
|
||||
py_name = _get_module_py_name(module_name)
|
||||
module_filename = '%s.py' % py_name
|
||||
var_name = _get_module_var_name(module_name)
|
||||
if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH,
|
||||
module_filename)) or update:
|
||||
_init()
|
||||
in_file_name = 'tmp0in439341018923js2py.js'
|
||||
out_file_name = 'tmp0out439341018923js2py.js'
|
||||
code = ADD_TO_GLOBALS_FUNC
|
||||
if include_polyfill:
|
||||
code += "\n;require('babel-polyfill');\n"
|
||||
code += """
|
||||
var module_temp_love_python = require(%s);
|
||||
addToGlobals(%s, module_temp_love_python);
|
||||
""" % (repr(module_name), repr(module_name))
|
||||
with open(os.path.join(DIRNAME, in_file_name), 'wb') as f:
|
||||
f.write(code.encode('utf-8') if six.PY3 else code)
|
||||
|
||||
pkg_name = module_name.partition('/')[0]
|
||||
# make sure the module is installed
|
||||
assert subprocess.call(
|
||||
'cd %s;npm install %s' % (repr(DIRNAME), pkg_name),
|
||||
shell=True,
|
||||
cwd=DIRNAME
|
||||
) == 0, 'Could not install the required module: ' + pkg_name
|
||||
|
||||
# convert the module
|
||||
assert subprocess.call(
|
||||
'''node -e "(require('browserify')('./%s').bundle(function (err,data) {if (err) {console.log(err);throw new Error(err);};fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"'''
|
||||
% (in_file_name, out_file_name),
|
||||
shell=True,
|
||||
cwd=DIRNAME,
|
||||
) == 0, 'Error when converting module to the js bundle'
|
||||
|
||||
os.remove(os.path.join(DIRNAME, in_file_name))
|
||||
with codecs.open(os.path.join(DIRNAME, out_file_name), "r",
|
||||
"utf-8") as f:
|
||||
js_code = f.read()
|
||||
os.remove(os.path.join(DIRNAME, out_file_name))
|
||||
if len(js_code) < 50:
|
||||
raise RuntimeError("Candidate JS bundle too short - likely browserify issue.")
|
||||
js_code += GET_FROM_GLOBALS_FUNC
|
||||
js_code += ';var %s = getFromGlobals(%s);%s' % (
|
||||
var_name, repr(module_name), var_name)
|
||||
print('Please wait, translating...')
|
||||
py_code = translate_js(js_code)
|
||||
|
||||
dirname = os.path.dirname(
|
||||
os.path.join(PY_NODE_MODULES_PATH, module_filename))
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
with open(os.path.join(PY_NODE_MODULES_PATH, module_filename),
|
||||
'wb') as f:
|
||||
f.write(py_code.encode('utf-8') if six.PY3 else py_code)
|
||||
else:
|
||||
with codecs.open(
|
||||
os.path.join(PY_NODE_MODULES_PATH, module_filename), "r",
|
||||
"utf-8") as f:
|
||||
py_code = f.read()
|
||||
return py_code
|
||||
|
||||
|
||||
def require(module_name, include_polyfill=False, update=False, context=None):
|
||||
"""
|
||||
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.
|
||||
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 include_polyfill: Whether the babel-polyfill should be included as part of the translation. May be needed
|
||||
for some modules that use unsupported features.
|
||||
: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
|
||||
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.
|
||||
"""
|
||||
py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=update)
|
||||
# this is a bit hacky but we need to strip the default header from the generated code...
|
||||
if context is not None:
|
||||
if not py_code.startswith(DEFAULT_HEADER):
|
||||
# new header version? retranslate...
|
||||
assert not update, "Unexpected header."
|
||||
py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=True)
|
||||
assert py_code.startswith(DEFAULT_HEADER), "Unexpected header."
|
||||
py_code = py_code[len(DEFAULT_HEADER):]
|
||||
context = {} if context is None else context
|
||||
exec (py_code, context)
|
||||
return context['var'][_get_module_var_name(module_name)].to_py()
|
||||
@@ -1,51 +0,0 @@
|
||||
# python 3 support
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
|
||||
class FunctionPrototype:
|
||||
def toString():
|
||||
if not this.is_callable():
|
||||
raise TypeError('toString is not generic!')
|
||||
args = ', '.join(this.code.__code__.co_varnames[:this.argcount])
|
||||
return 'function %s(%s) ' % (this.func_name, args) + this.source
|
||||
|
||||
def call():
|
||||
arguments_ = arguments
|
||||
if not len(arguments):
|
||||
obj = this.Js(None)
|
||||
else:
|
||||
obj = arguments[0]
|
||||
if len(arguments) <= 1:
|
||||
args = ()
|
||||
else:
|
||||
args = tuple([arguments_[e] for e in xrange(1, len(arguments_))])
|
||||
return this.call(obj, args)
|
||||
|
||||
def apply():
|
||||
if not len(arguments):
|
||||
obj = this.Js(None)
|
||||
else:
|
||||
obj = arguments[0]
|
||||
if len(arguments) <= 1:
|
||||
args = ()
|
||||
else:
|
||||
appl = arguments[1]
|
||||
args = tuple([appl[e] for e in xrange(len(appl))])
|
||||
return this.call(obj, args)
|
||||
|
||||
def bind(thisArg):
|
||||
arguments_ = arguments
|
||||
target = this
|
||||
if not target.is_callable():
|
||||
raise this.MakeError(
|
||||
'Object must be callable in order to be used with bind method')
|
||||
if len(arguments) <= 1:
|
||||
args = ()
|
||||
else:
|
||||
args = tuple([arguments_[e] for e in xrange(1, len(arguments_))])
|
||||
return this.PyJsBoundFunction(target, thisArg, args)
|
||||
Generated
-13682
File diff suppressed because one or more lines are too long
-4456
File diff suppressed because it is too large
Load Diff
-5673
File diff suppressed because one or more lines are too long
-7338
File diff suppressed because one or more lines are too long
Generated
-16405
File diff suppressed because one or more lines are too long
@@ -1,697 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from pyjsparser.pyjsparserdata import *
|
||||
from .friendly_nodes import *
|
||||
import random
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
from functools import reduce
|
||||
xrange = range
|
||||
unicode = str
|
||||
# number of characters above which expression will be split to multiple lines in order to avoid python parser stack overflow
|
||||
# still experimental so I suggest to set it to 400 in order to avoid common errors
|
||||
# set it to smaller value only if you have problems with parser stack overflow
|
||||
LINE_LEN_LIMIT = 400 # 200 # or any other value - the larger the smaller probability of errors :)
|
||||
|
||||
|
||||
class ForController:
|
||||
def __init__(self):
|
||||
self.inside = [False]
|
||||
self.update = ''
|
||||
|
||||
def enter_for(self, update):
|
||||
self.inside.append(True)
|
||||
self.update = update
|
||||
|
||||
def leave_for(self):
|
||||
self.inside.pop()
|
||||
|
||||
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:
|
||||
NAME = 'PyJs_%s_%d_'
|
||||
|
||||
def __init__(self):
|
||||
self.reps = {}
|
||||
self.names = []
|
||||
|
||||
def inject_inlines(self, source):
|
||||
for lval in self.names: # first in first out! Its important by the way
|
||||
source = inject_before_lval(source, lval, self.reps[lval])
|
||||
return source
|
||||
|
||||
def require(self, typ):
|
||||
name = self.NAME % (typ, len(self.names))
|
||||
self.names.append(name)
|
||||
return name
|
||||
|
||||
def define(self, name, val):
|
||||
self.reps[name] = val
|
||||
|
||||
def reset(self):
|
||||
self.rel = {}
|
||||
self.names = []
|
||||
|
||||
|
||||
class ContextStack:
|
||||
def __init__(self):
|
||||
self.to_register = set([])
|
||||
self.to_define = {}
|
||||
|
||||
def reset(self):
|
||||
self.to_register = set([])
|
||||
self.to_define = {}
|
||||
|
||||
def register(self, var):
|
||||
self.to_register.add(var)
|
||||
|
||||
def define(self, name, code):
|
||||
self.to_define[name] = code
|
||||
self.register(name)
|
||||
|
||||
def get_code(self):
|
||||
code = 'var.registers([%s])\n' % ', '.join(
|
||||
repr(e) for e in self.to_register)
|
||||
for name, func_code in six.iteritems(self.to_define):
|
||||
code += func_code
|
||||
return code
|
||||
|
||||
|
||||
def clean_stacks():
|
||||
global Context, inline_stack
|
||||
Context = ContextStack()
|
||||
inline_stack = InlineStack()
|
||||
|
||||
|
||||
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 'true' if k else 'false'
|
||||
elif k is None:
|
||||
return 'null'
|
||||
else:
|
||||
return unicode(k)
|
||||
|
||||
|
||||
def trans(ele, standard=False):
|
||||
"""Translates esprima syntax tree to python by delegating to appropriate translating node"""
|
||||
try:
|
||||
node = globals().get(ele['type'])
|
||||
if not node:
|
||||
raise NotImplementedError('%s is not supported!' % ele['type'])
|
||||
if standard:
|
||||
node = node.__dict__[
|
||||
'standard'] if 'standard' in node.__dict__ else node
|
||||
return node(**ele)
|
||||
except:
|
||||
#print ele
|
||||
raise
|
||||
|
||||
|
||||
def limited(func):
|
||||
'''Decorator limiting resulting line length in order to avoid python parser stack overflow -
|
||||
If expression longer than LINE_LEN_LIMIT characters then it will be moved to upper line
|
||||
USE ONLY ON EXPRESSIONS!!! '''
|
||||
|
||||
def f(standard=False, **args):
|
||||
insert_pos = len(
|
||||
inline_stack.names
|
||||
) # in case line is longer than limit we will have to insert the lval at current position
|
||||
# this is because calling func will change inline_stack.
|
||||
# we cant use inline_stack.require here because we dont know whether line overflows yet
|
||||
res = func(**args)
|
||||
if len(res) > LINE_LEN_LIMIT:
|
||||
name = inline_stack.require('LONG')
|
||||
inline_stack.names.pop()
|
||||
inline_stack.names.insert(insert_pos, name)
|
||||
res = 'def %s(var=var):\n return %s\n' % (name, res)
|
||||
inline_stack.define(name, res)
|
||||
return name + '()'
|
||||
else:
|
||||
return res
|
||||
|
||||
f.__dict__['standard'] = func
|
||||
return f
|
||||
|
||||
|
||||
# ==== IDENTIFIERS AND LITERALS =======
|
||||
|
||||
inf = float('inf')
|
||||
|
||||
|
||||
def Literal(type, value, raw, regex=None):
|
||||
if regex: # regex
|
||||
return 'JsRegExp(%s)' % repr(compose_regex(value))
|
||||
elif value is None: # null
|
||||
return 'var.get(u"null")'
|
||||
# Todo template
|
||||
# String, Bool, Float
|
||||
return 'Js(%s)' % repr(value) if value != inf else 'Js(float("inf"))'
|
||||
|
||||
|
||||
def Identifier(type, name):
|
||||
return 'var.get(%s)' % repr(name)
|
||||
|
||||
|
||||
@limited
|
||||
def MemberExpression(type, computed, object, property):
|
||||
far_left = trans(object)
|
||||
if computed: # obj[prop] type accessor
|
||||
# may be literal which is the same in every case so we can save some time on conversion
|
||||
if property['type'] == 'Literal':
|
||||
prop = repr(to_key(property))
|
||||
else: # worst case
|
||||
prop = trans(property)
|
||||
else: # always the same since not computed (obj.prop accessor)
|
||||
prop = repr(to_key(property))
|
||||
return far_left + '.get(%s)' % prop
|
||||
|
||||
|
||||
def ThisExpression(type):
|
||||
return 'var.get(u"this")'
|
||||
|
||||
|
||||
@limited
|
||||
def CallExpression(type, callee, arguments):
|
||||
arguments = [trans(e) for e in arguments]
|
||||
if callee['type'] == 'MemberExpression':
|
||||
far_left = trans(callee['object'])
|
||||
if callee['computed']: # obj[prop] type accessor
|
||||
# may be literal which is the same in every case so we can save some time on conversion
|
||||
if callee['property']['type'] == 'Literal':
|
||||
prop = repr(to_key(callee['property']))
|
||||
else: # worst case
|
||||
prop = trans(
|
||||
callee['property']) # its not a string literal! so no repr
|
||||
else: # always the same since not computed (obj.prop accessor)
|
||||
prop = repr(to_key(callee['property']))
|
||||
arguments.insert(0, prop)
|
||||
return far_left + '.callprop(%s)' % ', '.join(arguments)
|
||||
else: # standard call
|
||||
return trans(callee) + '(%s)' % ', '.join(arguments)
|
||||
|
||||
|
||||
# ========== ARRAYS ============
|
||||
|
||||
|
||||
def ArrayExpression(type, elements): # todo fix null inside problem
|
||||
return 'Js([%s])' % ', '.join(trans(e) if e else 'None' for e in elements)
|
||||
|
||||
|
||||
# ========== OBJECTS =============
|
||||
|
||||
|
||||
def ObjectExpression(type, properties):
|
||||
name = None
|
||||
elems = []
|
||||
after = ''
|
||||
for p in properties:
|
||||
if p['kind'] == 'init':
|
||||
elems.append('%s:%s' % Property(**p))
|
||||
else:
|
||||
if name is None:
|
||||
name = inline_stack.require('Object')
|
||||
if p['kind'] == 'set':
|
||||
k, setter = Property(
|
||||
**p
|
||||
) # setter is just a lval referring to that function, it will be defined in InlineStack automatically
|
||||
after += '%s.define_own_property(%s, {"set":%s, "configurable":True, "enumerable":True})\n' % (
|
||||
name, k, setter)
|
||||
elif p['kind'] == 'get':
|
||||
k, getter = Property(**p)
|
||||
after += '%s.define_own_property(%s, {"get":%s, "configurable":True, "enumerable":True})\n' % (
|
||||
name, k, getter)
|
||||
else:
|
||||
raise RuntimeError('Unexpected object propery kind')
|
||||
definition = 'Js({%s})' % ','.join(elems)
|
||||
if name is None:
|
||||
return definition
|
||||
body = '%s = %s\n' % (name, definition)
|
||||
body += after
|
||||
body += 'return %s\n' % name
|
||||
code = 'def %s():\n%s' % (name, indent(body))
|
||||
inline_stack.define(name, code)
|
||||
return name + '()'
|
||||
|
||||
|
||||
def Property(type, kind, key, computed, value, method, shorthand):
|
||||
if shorthand or computed:
|
||||
raise NotImplementedError(
|
||||
'Shorthand and Computed properties not implemented!')
|
||||
k = to_key(key)
|
||||
if k is None:
|
||||
raise SyntaxError('Invalid key in dictionary! Or bug in Js2Py')
|
||||
v = trans(value)
|
||||
return repr(k), v
|
||||
|
||||
|
||||
# ========== EXPRESSIONS ============
|
||||
|
||||
|
||||
@limited
|
||||
def UnaryExpression(type, operator, argument, prefix):
|
||||
a = trans(
|
||||
argument, standard=True
|
||||
) # unary involve some complex operations so we cant use line shorteners here
|
||||
if operator == 'delete':
|
||||
if argument['type'] in ('Identifier', 'MemberExpression'):
|
||||
# means that operation is valid
|
||||
return js_delete(a)
|
||||
return 'PyJsComma(%s, Js(True))' % a # otherwise not valid, just perform expression and return true.
|
||||
elif operator == 'typeof':
|
||||
return js_typeof(a)
|
||||
return UNARY[operator](a)
|
||||
|
||||
|
||||
@limited
|
||||
def BinaryExpression(type, operator, left, right):
|
||||
a = trans(left)
|
||||
b = trans(right)
|
||||
# delegate to our friends
|
||||
return BINARY[operator](a, b)
|
||||
|
||||
|
||||
@limited
|
||||
def UpdateExpression(type, operator, argument, prefix):
|
||||
a = trans(
|
||||
argument, standard=True
|
||||
) # also complex operation involving parsing of the result so no line length reducing here
|
||||
return js_postfix(a, operator == '++', not prefix)
|
||||
|
||||
|
||||
@limited
|
||||
def AssignmentExpression(type, operator, left, right):
|
||||
operator = operator[:-1]
|
||||
if left['type'] == 'Identifier':
|
||||
if operator:
|
||||
return 'var.put(%s, %s, %s)' % (repr(to_key(left)), trans(right),
|
||||
repr(operator))
|
||||
else:
|
||||
return 'var.put(%s, %s)' % (repr(to_key(left)), trans(right))
|
||||
elif left['type'] == 'MemberExpression':
|
||||
far_left = trans(left['object'])
|
||||
if left['computed']: # obj[prop] type accessor
|
||||
# may be literal which is the same in every case so we can save some time on conversion
|
||||
if left['property']['type'] == 'Literal':
|
||||
prop = repr(to_key(left['property']))
|
||||
else: # worst case
|
||||
prop = trans(
|
||||
left['property']) # its not a string literal! so no repr
|
||||
else: # always the same since not computed (obj.prop accessor)
|
||||
prop = repr(to_key(left['property']))
|
||||
if operator:
|
||||
return far_left + '.put(%s, %s, %s)' % (prop, trans(right),
|
||||
repr(operator))
|
||||
else:
|
||||
return far_left + '.put(%s, %s)' % (prop, trans(right))
|
||||
else:
|
||||
raise SyntaxError('Invalid left hand side in assignment!')
|
||||
|
||||
|
||||
six
|
||||
|
||||
|
||||
@limited
|
||||
def SequenceExpression(type, expressions):
|
||||
return reduce(js_comma, (trans(e) for e in expressions))
|
||||
|
||||
|
||||
@limited
|
||||
def NewExpression(type, callee, arguments):
|
||||
return trans(callee) + '.create(%s)' % ', '.join(
|
||||
trans(e) for e in arguments)
|
||||
|
||||
|
||||
@limited
|
||||
def ConditionalExpression(
|
||||
type, test, consequent,
|
||||
alternate): # caused plenty of problems in my home-made translator :)
|
||||
return '(%s if %s else %s)' % (trans(consequent), trans(test),
|
||||
trans(alternate))
|
||||
|
||||
|
||||
# =========== STATEMENTS =============
|
||||
|
||||
|
||||
def BlockStatement(type, body):
|
||||
return StatementList(
|
||||
body) # never returns empty string! In the worst case returns pass\n
|
||||
|
||||
|
||||
def ExpressionStatement(type, expression):
|
||||
return trans(expression) + '\n' # end expression space with new line
|
||||
|
||||
|
||||
def BreakStatement(type, label):
|
||||
if label:
|
||||
return 'raise %s("Breaked")\n' % (get_break_label(label['name']))
|
||||
else:
|
||||
return 'break\n'
|
||||
|
||||
|
||||
def ContinueStatement(type, label):
|
||||
if label:
|
||||
return 'raise %s("Continued")\n' % (get_continue_label(label['name']))
|
||||
else:
|
||||
return 'continue\n'
|
||||
|
||||
|
||||
def ReturnStatement(type, argument):
|
||||
return 'return %s\n' % (trans(argument)
|
||||
if argument else "var.get('undefined')")
|
||||
|
||||
|
||||
def EmptyStatement(type):
|
||||
return 'pass\n'
|
||||
|
||||
|
||||
def DebuggerStatement(type):
|
||||
return 'pass\n'
|
||||
|
||||
|
||||
def DoWhileStatement(type, body, test):
|
||||
inside = trans(body) + 'if not %s:\n' % trans(test) + indent('break\n')
|
||||
result = 'while 1:\n' + indent(inside)
|
||||
return result
|
||||
|
||||
|
||||
def ForStatement(type, init, test, update, body):
|
||||
update = indent(trans(update)) if update else ''
|
||||
init = trans(init) if init else ''
|
||||
if not init.endswith('\n'):
|
||||
init += '\n'
|
||||
test = trans(test) if test else '1'
|
||||
if not update:
|
||||
result = '#for JS loop\n%swhile %s:\n%s%s\n' % (
|
||||
init, test, indent(trans(body)), update)
|
||||
else:
|
||||
result = '#for JS loop\n%swhile %s:\n' % (init, test)
|
||||
body = 'try:\n%sfinally:\n %s\n' % (indent(trans(body)), update)
|
||||
result += indent(body)
|
||||
return result
|
||||
|
||||
|
||||
def ForInStatement(type, left, right, body, each):
|
||||
res = 'for PyJsTemp in %s:\n' % trans(right)
|
||||
if left['type'] == "VariableDeclaration":
|
||||
addon = trans(left) # make sure variable is registered
|
||||
if addon != 'pass\n':
|
||||
res = addon + res # we have to execute this expression :(
|
||||
# now extract the name
|
||||
try:
|
||||
name = left['declarations'][0]['id']['name']
|
||||
except:
|
||||
raise RuntimeError('Unusual ForIn loop')
|
||||
elif left['type'] == 'Identifier':
|
||||
name = left['name']
|
||||
else:
|
||||
raise RuntimeError('Unusual ForIn loop')
|
||||
res += indent('var.put(%s, PyJsTemp)\n' % repr(name) + trans(body))
|
||||
return res
|
||||
|
||||
|
||||
def IfStatement(type, test, consequent, alternate):
|
||||
# NOTE we cannot do elif because function definition inside elif statement would not be possible!
|
||||
IF = 'if %s:\n' % trans(test)
|
||||
IF += indent(trans(consequent))
|
||||
if not alternate:
|
||||
return IF
|
||||
ELSE = 'else:\n' + indent(trans(alternate))
|
||||
return IF + ELSE
|
||||
|
||||
|
||||
def LabeledStatement(type, label, body):
|
||||
# todo consider using smarter approach!
|
||||
inside = trans(body)
|
||||
defs = ''
|
||||
if inside.startswith('while ') or inside.startswith(
|
||||
'for ') or inside.startswith('#for'):
|
||||
# we have to add contine label as well...
|
||||
# 3 or 1 since #for loop type has more lines before real for.
|
||||
sep = 1 if not inside.startswith('#for') else 3
|
||||
cont_label = get_continue_label(label['name'])
|
||||
temp = inside.split('\n')
|
||||
injected = 'try:\n' + '\n'.join(temp[sep:])
|
||||
injected += 'except %s:\n pass\n' % cont_label
|
||||
inside = '\n'.join(temp[:sep]) + '\n' + indent(injected)
|
||||
defs += 'class %s(Exception): pass\n' % cont_label
|
||||
break_label = get_break_label(label['name'])
|
||||
inside = 'try:\n%sexcept %s:\n pass\n' % (indent(inside), break_label)
|
||||
defs += 'class %s(Exception): pass\n' % break_label
|
||||
return defs + inside
|
||||
|
||||
|
||||
def StatementList(lis):
|
||||
if lis: # ensure we don't return empty string because it may ruin indentation!
|
||||
code = ''.join(trans(e) for e in lis)
|
||||
return code if code else 'pass\n'
|
||||
else:
|
||||
return 'pass\n'
|
||||
|
||||
|
||||
def PyimportStatement(type, imp):
|
||||
lib = imp['name']
|
||||
jlib = 'PyImport_%s' % lib
|
||||
code = 'import %s as %s\n' % (lib, jlib)
|
||||
#check whether valid lib name...
|
||||
try:
|
||||
compile(code, '', 'exec')
|
||||
except:
|
||||
raise SyntaxError(
|
||||
'Invalid Python module name (%s) in pyimport statement' % lib)
|
||||
# var.pyimport will handle module conversion to PyJs object
|
||||
code += 'var.pyimport(%s, %s)\n' % (repr(lib), jlib)
|
||||
return code
|
||||
|
||||
|
||||
def SwitchStatement(type, discriminant, cases):
|
||||
#TODO there will be a problem with continue in a switch statement.... FIX IT
|
||||
code = 'while 1:\n' + indent('SWITCHED = False\nCONDITION = (%s)\n')
|
||||
code = code % trans(discriminant)
|
||||
for case in cases:
|
||||
case_code = None
|
||||
if case['test']: # case (x):
|
||||
case_code = 'if SWITCHED or PyJsStrictEq(CONDITION, %s):\n' % (
|
||||
trans(case['test']))
|
||||
else: # default:
|
||||
case_code = 'if True:\n'
|
||||
case_code += indent('SWITCHED = True\n')
|
||||
case_code += indent(StatementList(case['consequent']))
|
||||
# one more indent for whole
|
||||
code += indent(case_code)
|
||||
# prevent infinite loop and sort out nested switch...
|
||||
code += indent('SWITCHED = True\nbreak\n')
|
||||
return code
|
||||
|
||||
|
||||
def ThrowStatement(type, argument):
|
||||
return 'PyJsTempException = JsToPyException(%s)\nraise PyJsTempException\n' % trans(
|
||||
argument)
|
||||
|
||||
|
||||
def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer):
|
||||
result = 'try:\n%s' % indent(trans(block))
|
||||
# complicated catch statement...
|
||||
if handler:
|
||||
identifier = handler['param']['name']
|
||||
holder = 'PyJsHolder_%s_%d' % (to_hex(identifier),
|
||||
random.randrange(1e8))
|
||||
identifier = repr(identifier)
|
||||
result += 'except PyJsException as PyJsTempException:\n'
|
||||
# fill in except ( catch ) block and remember to recover holder variable to its previous state
|
||||
result += indent(
|
||||
TRY_CATCH.replace('HOLDER',
|
||||
holder).replace('NAME', identifier).replace(
|
||||
'BLOCK', indent(trans(handler['body']))))
|
||||
# translate finally statement if present
|
||||
if finalizer:
|
||||
result += 'finally:\n%s' % indent(trans(finalizer))
|
||||
return result
|
||||
|
||||
|
||||
def LexicalDeclaration(type, declarations, kind):
|
||||
raise NotImplementedError(
|
||||
'let and const not implemented yet but they will be soon! Check github for updates.'
|
||||
)
|
||||
|
||||
|
||||
def VariableDeclarator(type, id, init):
|
||||
name = id['name']
|
||||
# register the name if not already registered
|
||||
Context.register(name)
|
||||
if init:
|
||||
return 'var.put(%s, %s)\n' % (repr(name), trans(init))
|
||||
return ''
|
||||
|
||||
|
||||
def VariableDeclaration(type, declarations, kind):
|
||||
code = ''.join(trans(d) for d in declarations)
|
||||
return code if code else 'pass\n'
|
||||
|
||||
|
||||
def WhileStatement(type, test, body):
|
||||
result = 'while %s:\n' % trans(test) + indent(trans(body))
|
||||
return result
|
||||
|
||||
|
||||
def WithStatement(type, object, body):
|
||||
raise NotImplementedError('With statement not implemented!')
|
||||
|
||||
|
||||
def Program(type, body):
|
||||
inline_stack.reset()
|
||||
code = ''.join(trans(e) for e in body)
|
||||
# here add hoisted elements (register variables and define functions)
|
||||
code = Context.get_code() + code
|
||||
# replace all inline variables
|
||||
code = inline_stack.inject_inlines(code)
|
||||
return code
|
||||
|
||||
|
||||
# ======== FUNCTIONS ============
|
||||
|
||||
|
||||
def FunctionDeclaration(type, id, params, defaults, body, generator,
|
||||
expression):
|
||||
if generator:
|
||||
raise NotImplementedError('Generators not supported')
|
||||
if defaults:
|
||||
raise NotImplementedError('Defaults not supported')
|
||||
if not id:
|
||||
return FunctionExpression(type, id, params, defaults, body, generator,
|
||||
expression) + '\n'
|
||||
JsName = id['name']
|
||||
PyName = 'PyJsHoisted_%s_' % JsName
|
||||
PyName = PyName if is_valid_py_name(PyName) else 'PyJsHoistedNonPyName'
|
||||
# this is quite complicated
|
||||
global Context
|
||||
previous_context = Context
|
||||
# change context to the context of this function
|
||||
Context = ContextStack()
|
||||
# translate body within current context
|
||||
code = trans(body)
|
||||
# get arg names
|
||||
vars = [v['name'] for v in params]
|
||||
# args are automaticaly registered variables
|
||||
Context.to_register.update(vars)
|
||||
# add all hoisted elements inside function
|
||||
code = Context.get_code() + code
|
||||
# check whether args are valid python names:
|
||||
used_vars = []
|
||||
for v in vars:
|
||||
if is_valid_py_name(v):
|
||||
used_vars.append(v)
|
||||
else: # invalid arg in python, for example $, replace with alternatice arg
|
||||
used_vars.append('PyJsArg_%s_' % to_hex(v))
|
||||
header = '@Js\n'
|
||||
header += 'def %s(%sthis, arguments, var=var):\n' % (
|
||||
PyName, ', '.join(used_vars) + (', ' if vars else ''))
|
||||
# transfer names from Py scope to Js scope
|
||||
arg_map = dict(zip(vars, used_vars))
|
||||
arg_map.update({'this': 'this', 'arguments': 'arguments'})
|
||||
arg_conv = 'var = Scope({%s}, var)\n' % ', '.join(
|
||||
repr(k) + ':' + v for k, v in six.iteritems(arg_map))
|
||||
# and finally set the name of the function to its real name:
|
||||
footer = '%s.func_name = %s\n' % (PyName, repr(JsName))
|
||||
footer += 'var.put(%s, %s)\n' % (repr(JsName), PyName)
|
||||
whole_code = header + indent(arg_conv + code) + footer
|
||||
# restore context
|
||||
Context = previous_context
|
||||
# define in upper context
|
||||
Context.define(JsName, whole_code)
|
||||
return 'pass\n'
|
||||
|
||||
|
||||
def FunctionExpression(type, id, params, defaults, body, generator,
|
||||
expression):
|
||||
if generator:
|
||||
raise NotImplementedError('Generators not supported')
|
||||
if defaults:
|
||||
raise NotImplementedError('Defaults not supported')
|
||||
JsName = id['name'] if id else 'anonymous'
|
||||
if not is_valid_py_name(JsName):
|
||||
ScriptName = 'InlineNonPyName'
|
||||
else:
|
||||
ScriptName = JsName
|
||||
PyName = inline_stack.require(ScriptName) # this is unique
|
||||
|
||||
# again quite complicated
|
||||
global Context
|
||||
previous_context = Context
|
||||
# change context to the context of this function
|
||||
Context = ContextStack()
|
||||
# translate body within current context
|
||||
code = trans(body)
|
||||
# get arg names
|
||||
vars = [v['name'] for v in params]
|
||||
# args are automaticaly registered variables
|
||||
Context.to_register.update(vars)
|
||||
# add all hoisted elements inside function
|
||||
code = Context.get_code() + code
|
||||
# check whether args are valid python names:
|
||||
used_vars = []
|
||||
for v in vars:
|
||||
if is_valid_py_name(v):
|
||||
used_vars.append(v)
|
||||
else: # invalid arg in python, for example $, replace with alternatice arg
|
||||
used_vars.append('PyJsArg_%s_' % to_hex(v))
|
||||
header = '@Js\n'
|
||||
header += 'def %s(%sthis, arguments, var=var):\n' % (
|
||||
PyName, ', '.join(used_vars) + (', ' if vars else ''))
|
||||
# transfer names from Py scope to Js scope
|
||||
arg_map = dict(zip(vars, used_vars))
|
||||
arg_map.update({'this': 'this', 'arguments': 'arguments'})
|
||||
if id: # make self available from inside...
|
||||
if id['name'] not in arg_map:
|
||||
arg_map[id['name']] = PyName
|
||||
arg_conv = 'var = Scope({%s}, var)\n' % ', '.join(
|
||||
repr(k) + ':' + v for k, v in six.iteritems(arg_map))
|
||||
# and finally set the name of the function to its real name:
|
||||
footer = '%s._set_name(%s)\n' % (PyName, repr(JsName))
|
||||
whole_code = header + indent(arg_conv + code) + footer
|
||||
# restore context
|
||||
Context = previous_context
|
||||
# define in upper context
|
||||
inline_stack.define(PyName, whole_code)
|
||||
return PyName
|
||||
|
||||
|
||||
LogicalExpression = BinaryExpression
|
||||
PostfixExpression = UpdateExpression
|
||||
|
||||
clean_stacks()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import codecs
|
||||
import time
|
||||
import pyjsparser
|
||||
|
||||
c = None #'''`ijfdij`'''
|
||||
if not c:
|
||||
with codecs.open("esp.js", "r", "utf-8") as f:
|
||||
c = f.read()
|
||||
|
||||
print('Started')
|
||||
t = time.time()
|
||||
res = trans(pyjsparser.PyJsParser().parse(c))
|
||||
dt = time.time() - t + 0.000000001
|
||||
print('Translated everyting in', round(dt, 5), 'seconds.')
|
||||
print('Thats %d characters per second' % int(len(c) / dt))
|
||||
with open('res.py', 'w') as f:
|
||||
f.write(res)
|
||||
@@ -1,244 +0,0 @@
|
||||
__all__ = ['fix_js_args']
|
||||
|
||||
import types
|
||||
from collections import namedtuple
|
||||
import opcode
|
||||
import six
|
||||
import sys
|
||||
import dis
|
||||
|
||||
if six.PY3:
|
||||
xrange = range
|
||||
chr = lambda x: x
|
||||
|
||||
# Opcode constants used for comparison and replacecment
|
||||
LOAD_FAST = opcode.opmap['LOAD_FAST']
|
||||
LOAD_GLOBAL = opcode.opmap['LOAD_GLOBAL']
|
||||
STORE_FAST = opcode.opmap['STORE_FAST']
|
||||
|
||||
|
||||
def fix_js_args(func):
|
||||
'''Use this function when unsure whether func takes this and arguments as its last 2 args.
|
||||
It will append 2 args if it does not.'''
|
||||
fcode = six.get_function_code(func)
|
||||
fargs = fcode.co_varnames[fcode.co_argcount - 2:fcode.co_argcount]
|
||||
if fargs == ('this', 'arguments') or fargs == ('arguments', 'var'):
|
||||
return func
|
||||
code = append_arguments(six.get_function_code(func), ('this', 'arguments'))
|
||||
|
||||
return types.FunctionType(
|
||||
code,
|
||||
six.get_function_globals(func),
|
||||
func.__name__,
|
||||
closure=six.get_function_closure(func))
|
||||
|
||||
|
||||
def append_arguments(code_obj, new_locals):
|
||||
co_varnames = code_obj.co_varnames # Old locals
|
||||
co_names = code_obj.co_names # Old globals
|
||||
co_names += tuple(e for e in new_locals if e not in co_names)
|
||||
co_argcount = code_obj.co_argcount # Argument count
|
||||
co_code = code_obj.co_code # The actual bytecode as a string
|
||||
|
||||
# Make one pass over the bytecode to identify names that should be
|
||||
# left in code_obj.co_names.
|
||||
not_removed = set(opcode.hasname) - set([LOAD_GLOBAL])
|
||||
saved_names = set()
|
||||
for inst in instructions(code_obj):
|
||||
if inst[0] in not_removed:
|
||||
saved_names.add(co_names[inst[1]])
|
||||
|
||||
# Build co_names for the new code object. This should consist of
|
||||
# globals that were only accessed via LOAD_GLOBAL
|
||||
names = tuple(
|
||||
name for name in co_names if name not in set(new_locals) - saved_names)
|
||||
|
||||
# Build a dictionary that maps the indices of the entries in co_names
|
||||
# to their entry in the new co_names
|
||||
name_translations = dict(
|
||||
(co_names.index(name), i) for i, name in enumerate(names))
|
||||
|
||||
# Build co_varnames for the new code object. This should consist of
|
||||
# the entirety of co_varnames with new_locals spliced in after the
|
||||
# arguments
|
||||
new_locals_len = len(new_locals)
|
||||
varnames = (
|
||||
co_varnames[:co_argcount] + new_locals + co_varnames[co_argcount:])
|
||||
|
||||
# Build the dictionary that maps indices of entries in the old co_varnames
|
||||
# to their indices in the new co_varnames
|
||||
range1, range2 = xrange(co_argcount), xrange(co_argcount, len(co_varnames))
|
||||
varname_translations = dict((i, i) for i in range1)
|
||||
varname_translations.update((i, i + new_locals_len) for i in range2)
|
||||
|
||||
# Build the dictionary that maps indices of deleted entries of co_names
|
||||
# to their indices in the new co_varnames
|
||||
names_to_varnames = dict(
|
||||
(co_names.index(name), varnames.index(name)) for name in new_locals)
|
||||
|
||||
# Now we modify the actual bytecode
|
||||
modified = []
|
||||
for inst in instructions(code_obj):
|
||||
op, arg = inst.opcode, inst.arg
|
||||
# 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,
|
||||
# update its arg using the appropriate dict.
|
||||
if inst.opcode == LOAD_GLOBAL:
|
||||
if inst.arg in names_to_varnames:
|
||||
op = LOAD_FAST
|
||||
arg = names_to_varnames[inst.arg]
|
||||
elif inst.arg in name_translations:
|
||||
arg = name_translations[inst.arg]
|
||||
else:
|
||||
raise ValueError("a name was lost in translation")
|
||||
# If it accesses co_varnames or co_names then update its argument.
|
||||
elif inst.opcode in opcode.haslocal:
|
||||
arg = varname_translations[inst.arg]
|
||||
elif inst.opcode in opcode.hasname:
|
||||
arg = name_translations[inst.arg]
|
||||
modified.extend(write_instruction(op, arg))
|
||||
if six.PY2:
|
||||
code = ''.join(modified)
|
||||
args = (co_argcount + new_locals_len,
|
||||
code_obj.co_nlocals + new_locals_len, code_obj.co_stacksize,
|
||||
code_obj.co_flags, code, code_obj.co_consts, names, varnames,
|
||||
code_obj.co_filename, code_obj.co_name,
|
||||
code_obj.co_firstlineno, code_obj.co_lnotab,
|
||||
code_obj.co_freevars, code_obj.co_cellvars)
|
||||
else:
|
||||
code = bytes(modified)
|
||||
args = (co_argcount + new_locals_len, 0,
|
||||
code_obj.co_nlocals + new_locals_len, code_obj.co_stacksize,
|
||||
code_obj.co_flags, code, code_obj.co_consts, names, varnames,
|
||||
code_obj.co_filename, code_obj.co_name,
|
||||
code_obj.co_firstlineno, code_obj.co_lnotab,
|
||||
code_obj.co_freevars, code_obj.co_cellvars)
|
||||
|
||||
# Done modifying codestring - make the code object
|
||||
if hasattr(code_obj, "replace"):
|
||||
# Python 3.8+
|
||||
return code_obj.replace(
|
||||
co_argcount=co_argcount + new_locals_len,
|
||||
co_nlocals=code_obj.co_nlocals + new_locals_len,
|
||||
co_code=code,
|
||||
co_names=names,
|
||||
co_varnames=varnames)
|
||||
else:
|
||||
return types.CodeType(*args)
|
||||
|
||||
|
||||
def instructions(code_obj):
|
||||
# easy for python 3.4+
|
||||
if sys.version_info >= (3, 4):
|
||||
for inst in dis.Bytecode(code_obj):
|
||||
yield inst
|
||||
else:
|
||||
# otherwise we have to manually parse
|
||||
code = code_obj.co_code
|
||||
NewInstruction = namedtuple('Instruction', ('opcode', 'arg'))
|
||||
if six.PY2:
|
||||
code = map(ord, code)
|
||||
i, L = 0, len(code)
|
||||
extended_arg = 0
|
||||
while i < L:
|
||||
op = code[i]
|
||||
i += 1
|
||||
if op < opcode.HAVE_ARGUMENT:
|
||||
yield NewInstruction(op, None)
|
||||
continue
|
||||
oparg = code[i] + (code[i + 1] << 8) + extended_arg
|
||||
extended_arg = 0
|
||||
i += 2
|
||||
if op == opcode.EXTENDED_ARG:
|
||||
extended_arg = oparg << 16
|
||||
continue
|
||||
yield NewInstruction(op, oparg)
|
||||
|
||||
|
||||
def write_instruction(op, arg):
|
||||
if sys.version_info < (3, 6):
|
||||
if arg is None:
|
||||
return [chr(op)]
|
||||
elif arg <= 65536:
|
||||
return [chr(op), chr(arg & 255), chr((arg >> 8) & 255)]
|
||||
elif arg <= 4294967296:
|
||||
return [
|
||||
chr(opcode.EXTENDED_ARG),
|
||||
chr((arg >> 16) & 255),
|
||||
chr((arg >> 24) & 255),
|
||||
chr(op),
|
||||
chr(arg & 255),
|
||||
chr((arg >> 8) & 255)
|
||||
]
|
||||
else:
|
||||
raise ValueError("Invalid oparg: {0} is too large".format(oparg))
|
||||
else: # python 3.6+ uses wordcode instead of bytecode and they already supply all the EXTENDEND_ARG ops :)
|
||||
if arg is None:
|
||||
return [chr(op), 0]
|
||||
return [chr(op), arg & 255]
|
||||
# the code below is for case when extended args are to be determined automatically
|
||||
# if op == opcode.EXTENDED_ARG:
|
||||
# return [] # this will be added automatically
|
||||
# elif arg < 1 << 8:
|
||||
# return [chr(op), arg]
|
||||
# elif arg < 1 << 32:
|
||||
# subs = [1<<24, 1<<16, 1<<8] # allowed op extension sizes
|
||||
# for sub in subs:
|
||||
# if arg >= sub:
|
||||
# fit = int(arg / sub)
|
||||
# return [chr(opcode.EXTENDED_ARG), fit] + write_instruction(op, arg - fit * sub)
|
||||
# else:
|
||||
# raise ValueError("Invalid oparg: {0} is too large".format(oparg))
|
||||
|
||||
|
||||
def check(code_obj):
|
||||
old_bytecode = code_obj.co_code
|
||||
insts = list(instructions(code_obj))
|
||||
|
||||
pos_to_inst = {}
|
||||
bytelist = []
|
||||
|
||||
for inst in insts:
|
||||
pos_to_inst[len(bytelist)] = inst
|
||||
bytelist.extend(write_instruction(inst.opcode, inst.arg))
|
||||
if six.PY2:
|
||||
new_bytecode = ''.join(bytelist)
|
||||
else:
|
||||
new_bytecode = bytes(bytelist)
|
||||
if new_bytecode != old_bytecode:
|
||||
print(new_bytecode)
|
||||
print(old_bytecode)
|
||||
for i in range(min(len(new_bytecode), len(old_bytecode))):
|
||||
if old_bytecode[i] != new_bytecode[i]:
|
||||
while 1:
|
||||
if i in pos_to_inst:
|
||||
print(pos_to_inst[i])
|
||||
print(pos_to_inst[i - 2])
|
||||
print(list(map(chr, old_bytecode))[i - 4:i + 8])
|
||||
print(bytelist[i - 4:i + 8])
|
||||
break
|
||||
raise RuntimeError(
|
||||
'Your python version made changes to the bytecode')
|
||||
|
||||
|
||||
check(six.get_function_code(check))
|
||||
|
||||
if __name__ == '__main__':
|
||||
x = 'Wrong'
|
||||
dick = 3000
|
||||
|
||||
def func(a):
|
||||
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(
|
||||
append_arguments(six.get_function_code(func), ('x', 'y', 'z')),
|
||||
six.get_function_globals(func),
|
||||
func.__name__,
|
||||
closure=six.get_function_closure(func))
|
||||
args = (2, 2, 3, 4), 3, 4
|
||||
assert func2(1, *args) == args
|
||||
@@ -1,75 +0,0 @@
|
||||
# The MIT License
|
||||
#
|
||||
# Copyright 2014, 2015 Piotr Dabkowski
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the 'Software'),
|
||||
# to deal in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so, subject
|
||||
# to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all copies or
|
||||
# substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
""" This module allows you to translate and execute Javascript in pure python.
|
||||
Basically its implementation of ECMAScript 5.1 in pure python.
|
||||
|
||||
Use eval_js method to execute javascript code and get resulting python object (builtin if possible).
|
||||
|
||||
EXAMPLE:
|
||||
>>> import js2py
|
||||
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
||||
>>> add(1, 2) + 3
|
||||
6
|
||||
>>> add('1', 2, 3)
|
||||
u'12'
|
||||
>>> add.constructor
|
||||
function Function() { [python code] }
|
||||
|
||||
|
||||
Or use EvalJs to execute many javascript code fragments under same context - you would be able to get any
|
||||
variable from the context!
|
||||
|
||||
>>> js = js2py.EvalJs()
|
||||
>>> js.execute('var a = 10; function f(x) {return x*x};')
|
||||
>>> js.f(9)
|
||||
81
|
||||
>>> js.a
|
||||
10
|
||||
|
||||
Also you can use its console method to play with interactive javascript console.
|
||||
|
||||
|
||||
Use parse_js to parse (syntax tree is just like in esprima.js) and translate_js to trasnlate JavaScript.
|
||||
|
||||
Finally, you can use pyimport statement from inside JS code to import and use python libraries.
|
||||
|
||||
>>> js2py.eval_js('pyimport urllib; urllib.urlopen("https://www.google.com")')
|
||||
|
||||
NOTE: This module is still not fully finished:
|
||||
|
||||
Date and JSON builtin objects are not implemented
|
||||
Array prototype is not fully finished (will be soon)
|
||||
|
||||
Other than that everything should work fine.
|
||||
|
||||
"""
|
||||
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
__all__ = [
|
||||
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'parse_js',
|
||||
'translate_file', 'run_file', 'disable_pyimport', 'eval_js6',
|
||||
'translate_js6', 'PyJsException', 'get_file_contents',
|
||||
'write_file_contents', 'require'
|
||||
]
|
||||
|
||||
from .base import PyJsException
|
||||
from .evaljs import *
|
||||
from .translators import parse as parse_js
|
||||
from .node_import import require
|
||||
@@ -1 +0,0 @@
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
@@ -1,48 +0,0 @@
|
||||
from ..base import *
|
||||
|
||||
|
||||
@Js
|
||||
def Array():
|
||||
if len(arguments) == 0 or len(arguments) > 1:
|
||||
return arguments.to_list()
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber):
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js([])
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
return [a]
|
||||
|
||||
|
||||
Array.create = Array
|
||||
Array.own['length']['value'] = Js(1)
|
||||
|
||||
|
||||
@Js
|
||||
def isArray(arg):
|
||||
return arg.Class == 'Array'
|
||||
|
||||
|
||||
Array.define_own_property('isArray', {
|
||||
'value': isArray,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
ArrayPrototype.define_own_property('constructor', {
|
||||
'value': Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
# todo check everything :)
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def ArrayBuffer():
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber):
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(bytearray([0] * length))
|
||||
return temp
|
||||
return Js(bytearray([0]))
|
||||
|
||||
|
||||
ArrayBuffer.create = ArrayBuffer
|
||||
ArrayBuffer.own['length']['value'] = Js(None)
|
||||
|
||||
ArrayBuffer.define_own_property(
|
||||
'prototype', {
|
||||
'value': ArrayBufferPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
ArrayBufferPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': ArrayBuffer,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
from ..base import *
|
||||
|
||||
BooleanPrototype.define_own_property('constructor', {
|
||||
'value': Boolean,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Boolean.define_own_property(
|
||||
'prototype', {
|
||||
'value': BooleanPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -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,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Float32Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.float32))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.float32))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.float32))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 4 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of Float32Array should be a multiple of 4')
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
if offset % 4 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Start offset of Float32Array should be a multiple of 4')
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int((len(a.obj) - offset) / 4)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.float32, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.float32))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Float32Array.create = Float32Array
|
||||
Float32Array.own['length']['value'] = Js(3)
|
||||
|
||||
Float32Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Float32ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Float32ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Float32Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Float32ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(4),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Float64Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.float64))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.float64))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.float64))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 8 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of Float64Array should be a multiple of 8')
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
if offset % 8 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Start offset of Float64Array should be a multiple of 8')
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int((len(a.obj) - offset) / 8)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.float64, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.float64))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Float64Array.create = Float64Array
|
||||
Float64Array.own['length']['value'] = Js(3)
|
||||
|
||||
Float64Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Float64ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Float64ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Float64Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Float64ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(8),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,52 +0,0 @@
|
||||
from ..base import *
|
||||
try:
|
||||
from ..translators.translator import translate_js
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Function():
|
||||
# convert arguments to python list of strings
|
||||
a = [e.to_string().value for e in arguments.to_list()]
|
||||
body = ';'
|
||||
args = ()
|
||||
if len(a):
|
||||
body = '%s;' % a[-1]
|
||||
args = a[:-1]
|
||||
# translate this function to js inline function
|
||||
js_func = '(function (%s) {%s})' % (','.join(args), body)
|
||||
# now translate js inline to python function
|
||||
py_func = translate_js(js_func, '')
|
||||
# add set func scope to global scope
|
||||
# a but messy solution but works :)
|
||||
globals()['var'] = PyJs.GlobalObject
|
||||
# define py function and return it
|
||||
temp = executor(py_func, globals())
|
||||
temp.source = '{%s}' % body
|
||||
temp.func_name = 'anonymous'
|
||||
return temp
|
||||
|
||||
|
||||
def executor(f, glob):
|
||||
exec (f, globals())
|
||||
return globals()['PyJs_anonymous_0_']
|
||||
|
||||
|
||||
#new statement simply calls Function
|
||||
Function.create = Function
|
||||
|
||||
#set constructor property inside FunctionPrototype
|
||||
|
||||
fill_in_props(FunctionPrototype, {'constructor': Function}, default_attrs)
|
||||
|
||||
#attach prototype to Function constructor
|
||||
Function.define_own_property(
|
||||
'prototype', {
|
||||
'value': FunctionPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
#Fix Function length (its 0 and should be 1)
|
||||
Function.own['length']['value'] = Js(1)
|
||||
@@ -1,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Int16Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.int16))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.int16))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.int16))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 2 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of Int16Array should be a multiple of 2')
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
if offset % 2 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Start offset of Int16Array should be a multiple of 2')
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int((len(a.obj) - offset) / 2)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.int16, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.int16))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Int16Array.create = Int16Array
|
||||
Int16Array.own['length']['value'] = Js(3)
|
||||
|
||||
Int16Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Int16ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Int16ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Int16Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Int16ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(2),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Int32Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.int32))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.int32))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.int32))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 4 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of Int32Array should be a multiple of 4')
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
if offset % 4 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Start offset of Int32Array should be a multiple of 4')
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int((len(a.obj) - offset) / 4)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.int32, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.int32))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Int32Array.create = Int32Array
|
||||
Int32Array.own['length']['value'] = Js(3)
|
||||
|
||||
Int32Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Int32ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Int32ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Int32Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Int32ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(4),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,79 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Int8Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.int8))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.int8))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.int8))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int(len(a.obj) - offset)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.int8, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.int8))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Int8Array.create = Int8Array
|
||||
Int8Array.own['length']['value'] = Js(3)
|
||||
|
||||
Int8Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Int8ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Int8ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Int8Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Int8ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(1),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,157 +0,0 @@
|
||||
from ..base import *
|
||||
import math
|
||||
import random
|
||||
|
||||
Math = PyJsObject(prototype=ObjectPrototype)
|
||||
Math.Class = 'Math'
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
for constant, value in CONSTANTS.items():
|
||||
Math.define_own_property(
|
||||
constant, {
|
||||
'value': Js(value),
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
|
||||
class MathFunctions:
|
||||
def abs(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return abs(a)
|
||||
|
||||
def acos(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.acos(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def asin(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.asin(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def atan(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.atan(a)
|
||||
|
||||
def atan2(y, x):
|
||||
a = x.to_number().value
|
||||
b = y.to_number().value
|
||||
if a != a or b != b: # it must be a nan
|
||||
return NaN
|
||||
return math.atan2(b, a)
|
||||
|
||||
def ceil(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.ceil(a)
|
||||
|
||||
def floor(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.floor(a)
|
||||
|
||||
def round(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return round(a)
|
||||
|
||||
def sin(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.sin(a)
|
||||
|
||||
def cos(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.cos(a)
|
||||
|
||||
def tan(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.tan(a)
|
||||
|
||||
def log(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.log(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def exp(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.exp(a)
|
||||
|
||||
def pow(x, y):
|
||||
a = x.to_number().value
|
||||
b = y.to_number().value
|
||||
if a != a or b != b: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return a**b
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def sqrt(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return a**0.5
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def min():
|
||||
if not len(arguments):
|
||||
return Infinity
|
||||
lis = tuple(e.to_number().value for e in arguments.to_list())
|
||||
if any(e != e for e in lis): # we dont want NaNs
|
||||
return NaN
|
||||
return min(*lis)
|
||||
|
||||
def max():
|
||||
if not len(arguments):
|
||||
return -Infinity
|
||||
lis = tuple(e.to_number().value for e in arguments.to_list())
|
||||
if any(e != e for e in lis): # we dont want NaNs
|
||||
return NaN
|
||||
return max(*lis)
|
||||
|
||||
def random():
|
||||
return random.random()
|
||||
|
||||
|
||||
fill_prototype(Math, MathFunctions, default_attrs)
|
||||
@@ -1,23 +0,0 @@
|
||||
from ..base import *
|
||||
|
||||
CONSTS = {
|
||||
'prototype': NumberPrototype,
|
||||
'MAX_VALUE': 1.7976931348623157e308,
|
||||
'MIN_VALUE': 5.0e-324,
|
||||
'NaN': NaN,
|
||||
'NEGATIVE_INFINITY': float('-inf'),
|
||||
'POSITIVE_INFINITY': float('inf')
|
||||
}
|
||||
|
||||
fill_in_props(Number, CONSTS, {
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
NumberPrototype.define_own_property('constructor', {
|
||||
'value': Number,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user