Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
9fdd62f
Merge pull request #85 from PiotrDabkowski/JsInPy
Jul 4, 2017
30bb0ab
Exposed the vm
PiotrDabkowski Aug 3, 2017
4d96977
mend
PiotrDabkowski Aug 3, 2017
50f828f
py3
PiotrDabkowski Aug 3, 2017
879b02a
Update README.md
Oct 14, 2017
47b7c7c
Delete interface.py
Nov 22, 2017
4337238
Delete constants.py
Nov 22, 2017
1896022
Create .codeclimate.yml
Nov 22, 2017
1ac7167
added require (allows to import any supported lib from npm)
Dec 12, 2017
d4220c1
small fix
PiotrDabkowski Dec 12, 2017
0f3d4bd
should work now...
PiotrDabkowski Dec 12, 2017
a5d8f09
fixed es5 generation
PiotrDabkowski Dec 14, 2017
c8a86f7
added uri functions
PiotrDabkowski Dec 16, 2017
0a55eef
now you can import any node module is if it was written in python!
PiotrDabkowski Dec 17, 2017
9c83062
Update README.md
Dec 17, 2017
1358295
node import fix
PiotrDabkowski Dec 17, 2017
aad00e8
Support RFC3339 dates without microseconds
remko Dec 21, 2017
cd3fb43
js2py.require support path
qwIvan Jan 15, 2018
6c0d476
remove empty line
qwIvan Jan 15, 2018
21e9ea8
add empty lin
qwIvan Jan 15, 2018
aa74bfd
add empty line
qwIvan Jan 15, 2018
f8cdb7d
support for py2
qwIvan Jan 15, 2018
7e74f04
Merge pull request #102 from qwIvan/master
Jan 15, 2018
b6dc200
Merge pull request #99 from remko/master
Jan 25, 2018
c393e5e
Cast start argument to int
czukowski Jan 26, 2018
1898e9e
Check for object has been applied to wrong argument
czukowski Jan 26, 2018
8a3d9a2
Merge pull request #110 from czukowski/issue-108
Jan 26, 2018
240cd36
Merge pull request #109 from czukowski/issue-107
Jan 26, 2018
b1bbbeb
fixes #111
PiotrDabkowski Jan 28, 2018
a0d3369
Swap binary op arguments in STORE_MEMBER_OP and STORE_MEMBER_DOT_OP
czukowski Feb 4, 2018
8a2edcc
Add space argument to recursive convert_to_js_type calls
czukowski Feb 4, 2018
135620e
Merge pull request #115 from czukowski/issue-114
Feb 4, 2018
62a5bba
Merge pull request #116 from czukowski/issue-113
Feb 4, 2018
af268c4
vm fixes and for-in implementation
PiotrDabkowski Feb 5, 2018
1306275
Fixed PyObjectWrapper, assignment op operator
PiotrDabkowski Sep 3, 2018
0992e51
yapf
PiotrDabkowski Nov 4, 2018
cfb315a
Update README.md
Nov 17, 2018
e674a95
Make compatible with Python3
rjungbeck Nov 21, 2018
2737693
Merge pull request #140 from rjungbeck/patch-1
Nov 21, 2018
87cb410
Make compatible with Python 3
rjungbeck Nov 21, 2018
da310bb
Merge pull request #141 from rjungbeck/patch-2
Nov 21, 2018
0610132
console: add methods debug,info,warn,error
Dec 30, 2018
4ddc248
Fix several DeprecationWarning: invalid escape sequence
BoboTiG Jan 2, 2019
b52d2d8
Convert js objects to imperfect python primitives implicitly (if requ…
PiotrDabkowski Jan 3, 2019
c328582
Merge branch 'master' of https://github.com/PiotrDabkowski/Js2Py
PiotrDabkowski Jan 3, 2019
7a3a1ff
Add a function returning js bytecode.
Mar 9, 2019
8057725
vm improvements
PiotrDabkowski Apr 13, 2019
789af48
Convert exponential numbers to string ecma-262/5.1/#sec-9.8.1
invalid-email-address Apr 7, 2019
f6be867
dtoa
PiotrDabkowski Apr 18, 2019
ba37b4b
Fix inconsistency between py2 and py3 jsdtoa
Apr 20, 2019
c0fa43f
fix 1e-4 1e-6 range
PiotrDabkowski Apr 20, 2019
b5fbaaf
Fix injected local 'arguments' not working in list comprehension in b…
SpencerPark May 1, 2019
efbfcca
fix object initialized too early case
PiotrDabkowski May 14, 2019
74d4652
Fix injector for Python 3.8+.
PetterS Jun 15, 2019
d7a0321
Try to add Python 3.7 and 3.8 (dev).
PetterS Jun 16, 2019
a59d743
Upgrade Travis CI to a xenial image.
PetterS Jun 16, 2019
80bc087
Drop support for unsupported Python versions.
PetterS Jun 16, 2019
7858d1d
add require support from within js, fix browserify issue
PiotrDabkowski Jun 16, 2019
5709e29
fix rare py3 error
PiotrDabkowski Dec 24, 2019
12739ed
fix babel travis
PiotrDabkowski Dec 24, 2019
11ae438
Fix print functions and a python2 only import
mvanbaak Feb 11, 2020
46888c3
add support for require module versions, fix travis
PiotrDabkowski Mar 6, 2020
04241ba
fix error msg from python function beacuase python3+ does not support…
Mar 5, 2020
1158449
release + vm fixes
PiotrDabkowski Mar 7, 2020
55d6bf3
improve undefined is not a function error message (finally)
PiotrDabkowski Apr 11, 2020
f297498
Fix labeled block statement pretending to be a loop.
PiotrDabkowski Apr 25, 2020
e7e3d56
Update .travis.yml
Sep 18, 2020
d29d9d0
Complete all setters!
worstperson Sep 17, 2020
318d87d
fix mistake
worstperson Sep 17, 2020
483f932
Have Date.UTC return a number
worstperson Sep 20, 2020
92250a4
Workaround limits of datetime.datetime.utcfromtimestamp
worstperson Sep 20, 2020
a7dac6b
Remove idiotic codeclimate
Sep 25, 2020
042bf23
[WIN] Fix DaylightSavingTA
worstperson Sep 25, 2020
9d89657
[WIN] Fix mirrored code in internals/base.py
worstperson Sep 25, 2020
e89cb94
Adding property context to EvalJs
ralphwetzel Jan 3, 2021
f1e5b9c
Adds basic support for JS input date formats
serpulga Feb 16, 2021
ea16b51
Supports datetimes withouth Z indicator
serpulga Feb 18, 2021
5f665f6
install the npm libraries in the tmp folder
PiotrDabkowski Apr 1, 2021
82ad65c
setup.cfg: description-file -> description_file
Jun 22, 2021
7ec6151
Found BUG of translate_file and fixed
GasinAn Jan 11, 2020
1980384
fix String.replace(regex, '&$').
bjones1 Sep 12, 2021
d02ebf2
Use threading.is_alive instead of older threading.isAlive name.
bjones1 Sep 12, 2021
630da5a
Add github actions workflow
4144 Aug 15, 2021
19cce6a
Fix for loop update after break
PiotrDabkowski Sep 16, 2021
b16d7ce
fix py2 print
PiotrDabkowski Sep 18, 2021
70fe4f5
support python 3.11 bytecode, fixes #282
PiotrDabkowski Nov 6, 2022
2e017b8
remove template and opname not supported in old python version
PiotrDabkowski Nov 6, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: tests

on: [push, pull_request]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# disabled windows due node packages issues
# os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest]
python-version: [ '2.x', '3.x', '3.5', '3.6', '3.7', '3.8', '3.9', 'pypy-2.7', 'pypy-3.6']
exclude:
- os: windows-latest
python-version: '3.5'
- os: macos-latest
python-version: 'pypy-3.6'
name: ${{ matrix.os }} ${{ matrix.arch }}, Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v2
- name: Setup python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Python version
run: python --version
- name: Install packages
run: pip install -r requirements.txt && pip install numpy
- name: Simple tests
run: python simple_test.py
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Icon
# Files that might appear on external disk
.Spotlight-V100
.Trashes
venv

# Directories potentially created on remote AFP share
.AppleDB
Expand Down Expand Up @@ -108,3 +109,11 @@ $RECYCLE.BIN/
.idea/dictionaries/
.idea/inspectionProfiles/
.idea/scopes/

bench.py
out.py
sample.js
package-lock.json
js2py/node_modules
js2py/py_node_modules/*
!js2py/py_node_modules/__init__.py
11 changes: 6 additions & 5 deletions .travis.yml
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
dist: xenial
language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
- "3.7"
- "3.8"
- "3.8-dev"
# command to install dependencies!
install: "pip install -r requirements.txt"
install: "pip install -r requirements.txt && pip install numpy"
# command to run tests!
script: python simple_test.py
script: python simple_test.py
143 changes: 125 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Build Status](https://travis-ci.org/PiotrDabkowski/Js2Py.svg?branch=master)](https://travis-ci.org/PiotrDabkowski/Js2Py)
[![Build Status](https://travis-ci.org/PiotrDabkowski/Js2Py.svg?branch=master)](https://travis-ci.org/PiotrDabkowski/Js2Py) [![Downloads](https://pepy.tech/badge/js2py/month)](https://pepy.tech/project/js2py)

#### Pure Python JavaScript Translator/Interpreter

Expand All @@ -16,7 +16,24 @@ Simple Example:
6
>>> add.constructor
function Function() { [python code] }
>>> js2py.require('underscore')
'function _(obj) { [python code] }'
```
You can also import a big number of node modules as if they were written in Python!
For example, here we import a pure JS library [crypto-js](https://www.npmjs.com/package/crypto-js):

```python
>>> CryptoJS = js2py.require('crypto-js')
>>> data = [{'id': 1}, {'id': 2}]
>>> JSON = js2py.eval_js('JSON')
>>> ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123')
>>> bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'secret key 123')
>>> decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list()
>>> decryptedData
[{u'id': 1}, {u'id': 2}]
```



Now also supports JavaScript 6 (still experimental):

Expand Down Expand Up @@ -47,7 +64,12 @@ Every feature of ECMA 5.1 is implemented (except of 'with' statement):
```
Unfortunately even though Js2Py can be generally used to translate huge Js files (over 50k lines long), in rare cases you may encounter some unexpected problems (like javascript calling a function with 300 arguments - python allows only 255). These problems are very hard to fix with current translation approach. I will try to implement an interpreter in near future which will hopefully fix all the edge cases.

#### Installation

pip install js2py

<hr>

#### More advanced usage example

It is possible to access all the variables from JS scope using EvalJs. Moreover, you can use Python objects from your JavaScript code if you add them to the scope.
Expand Down Expand Up @@ -83,26 +105,95 @@ function bind(thisArg) { [python code] }
6
```

You can also enable require support in JavaScript like this:

```python
>>> context = js2py.EvalJs(enable_require=True)
>>> context.eval("require('esprima').parse('var a = 1')")
```
<hr>

### JavaScript 'VirtualMachine' in Python

As a fun experimental project I have also implemented a VM-based JavaScript
(yes - there are 2 separate JS implementations in this repo). It is feature complete and faster than the translation based version.
Below you can see a demo with a nice debug view (bytecode + execution sequence):

```python
>>> from js2py.internals import seval
>>> seval.eval_js_vm("try {throw 3+3} catch (e) {console.log(e)}", debug=True)
[LOAD_UNDEFINED(),
JUMP(4,),
LABEL(1,),
LOAD_UNDEFINED(),
POP(),
LOAD_NUMBER(3.0,),
LOAD_NUMBER(3.0,),
BINARY_OP('+',),
THROW(),
NOP(),
LABEL(2,),
LOAD_UNDEFINED(),
POP(),
LOAD('console',),
LOAD('e',),
LOAD_N_TUPLE(1,),
CALL_METHOD_DOT('log',),
NOP(),
LABEL(3,),
LOAD_UNDEFINED(),
NOP(),
LABEL(4,),
TRY_CATCH_FINALLY(1, 2, 'e', 3, False, 4)]

0 LOAD_UNDEFINED()
1 JUMP(4,)
18 TRY_CATCH_FINALLY(1, 2, 'e', 3, False, 4)
ctx entry (from:2, to:9)
2 LOAD_UNDEFINED()
3 POP()
4 LOAD_NUMBER(3.0,)
5 LOAD_NUMBER(3.0,)
6 BINARY_OP('+',)
7 THROW()
ctx exit (js errors)
ctx entry (from:9, to:16)
9 LOAD_UNDEFINED()
10 POP()
11 LOAD('console',)
12 LOAD('e',)
13 LOAD_N_TUPLE(1,)
14 CALL_METHOD_DOT('log',)
6
15 NOP()
ctx exit (normal)

```

This is just a curiosity and I do not recommend using VM in practice (requires more polishing).

<hr>

#### Limitations

It has only 3 known limitations:
There are 3 main limitations:
<ul>
<li>"strict mode" is ignored</li>
<li>with statement is not supported</li>
<li>Indirect call to eval is treated as direct call to eval (hence always evals in local scope)</li>
</ul>

Please let me know if you find any bugs - they will be fixed within 48 hours.
They are generally not a big issue in practice.
In practice more problematic are minor edge cases that unfortunately
sometimes do happen. Please report a bug if you find one.

Js2Py was able to successfully
translate and run huge JS libraries like Babel (100k+ loc), esprima, crypto-js and more.
You can try it yourself by importing any supported npm package via `js2py.require('your_package')`.

<hr>

#### Installation

pip install js2py

<hr>

#### Other Examples

Expand Down Expand Up @@ -153,26 +244,42 @@ Also, of course you can use Js2Py to parse (tree is the same as in esprima.js) a
#### Parsing:
```python
>>> js2py.parse_js('var $ = 5')
{'body': [{'kind': 'var', 'declarations': [{'init': {'raw': None, 'type': u'Literal', 'value': 5.0}, 'type': u'VariableDeclarator', 'id': {'type': u'Identifier', 'name': u'$'}}], 'type': u'VariableDeclaration'}], 'type': u'Program'}
{
"body": [
{
"declarations": [
{
"id": {
"name": "$",
"type": "Identifier"
},
"init": {
"raw": "5",
"type": "Literal",
"value": 5
},
"type": "VariableDeclarator"
}
],
"kind": "var",
"type": "VariableDeclaration"
}
],
"type": "Program"
}
```
#### Translating:

```python
>>> print js2py.translate_js('var $ = 5')
import js2py.pyjs, sys
# Redefine builtin objects... Do you have a better idea?
for m in sys.modules.keys():
if m.startswith('js2py'):
del sys.modules[m]
del js2py.pyjs
del js2py
>>> print(js2py.translate_js('var $ = 5'))
from js2py.pyjs import *
# setting scope
var = Scope( JS_BUILTINS )
set_global_object(var)

# Code follows:
var.registers([u'$'])
var.put(u'$', Js(5.0))
var.registers(['$'])
var.put('$', Js(5.0))
```
<hr>

Expand Down
13 changes: 13 additions & 0 deletions bench.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3.11-alpine
WORKDIR /app
RUN pip3 install pyjsparser six
RUN pip3 install tzlocal
RUN apk add --update alpine-sdk
RUN apk --no-cache --update add build-base
RUN pip3 install numpy

ADD . /app

RUN ls
#CMD ["python3", "js2py/utils/injector.py"]
CMD ["python3", "simple_test.py"]
82 changes: 82 additions & 0 deletions ev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
function __get_event_env__() {
//pyimport time;
var id = 0;
var sh = [];
function add_event(func, args, interval, id) {
var ev = [func, args, time.time()+interval/1000., id];
sh.push(ev);
}
function clear_event(id) {
var to_del = false;
for (var i=0; i<sh.length; i++) {
if (sh[i][3] === +id) {
to_del = i;
break;
}
}
if (to_del !== false) {
sh.splice(to_del, 1);
}
}
function exec_one() {
var t = time.time();
for (var i=0; i<sh.length; i++) {
if (sh[i][2] <= t) {
try {
sh[0].call(this, sh[1]);
} catch (e) {
// todo what should we do in case of errors?
}
clear_event(sh[3]);
break
}
}
}
function setTimeout(func, interval) {
var args = [];
var my_id = ++id;
add_event(func, args, interval, my_id);
return my_id // todo return a full timeout object
}
function setInterval(func, interval) {
var args = [];
var my_id = ++id;
function wrap() {
try {
func.call(this, args);
} finally {
add_event(wrap, [], interval, my_id)
}
}
add_event(wrap, [], interval, my_id);
return my_id // todo return a full timeout object
}

function event_loop() {
while (true) {
if (sh.length===0) {
break
}
exec_one();
time.sleep(0.001);

}
}
function clearInterval(timeoutObj) {
clear_event(timeoutObj);
}
var clearTimeout = clearInterval;

return [setTimeout, clearTimeout, setInterval, clearInterval, event_loop]
}
__event_env__ = __get_event_env__();
var setTimeout = __event_env__[0];
var clearTimeout = __event_env__[1];
var setInterval = __event_env__[2];
var clearInterval = __event_env__[3];
var __event_loop__ = __event_env__[4];

setTimeout(function h() {console.log('aaa')}, 1000);


__event_loop__()
8 changes: 8 additions & 0 deletions examples/importing_node_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# You can import any compatible node module, similar to npm install module & require(module) in node
from js2py import require

# This will automatically install and translate everything. Subsequent requires are fast because translation is cached.
random_int = require('random-int') # https://www.npmjs.com/package/random-int

print(random_int)
print(random_int(10, 40))
Loading