From 30bb0abfe37b65c912145a02070f5c478dda68be Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Thu, 3 Aug 2017 10:13:40 +0200 Subject: [PATCH 01/77] Exposed the vm --- ev.js | 82 +++++++++++++++++++ js2py/base.py | 11 ++- js2py/evaljs.py | 2 + js2py/internals/base.py | 68 ++++++++------- js2py/internals/byte_trans.py | 27 +++++- js2py/internals/code.py | 11 +-- js2py/internals/constructors/jsfunction.py | 40 ++++----- js2py/internals/fill_space.py | 2 + js2py/internals/opcodes.py | 47 +++++++++-- js2py/internals/operations.py | 9 +- js2py/internals/prototypes/jsnumber.py | 12 +-- js2py/internals/seval.py | 28 +++++++ js2py/internals/simplex.py | 29 +++++-- js2py/internals/trans_utils.py | 1 + js2py/test_internals.py | 8 +- js2py/translators/translator.py | 1 - setup.py | 4 +- test_bench.py | 0 tests/{run_on_js2py.py => run.py} | 56 +++++++++---- tests/test_cases/language/asi/S7.9_A5.7_T1.js | 23 ------ 20 files changed, 333 insertions(+), 128 deletions(-) create mode 100644 ev.js create mode 100644 js2py/internals/seval.py delete mode 100644 test_bench.py rename tests/{run_on_js2py.py => run.py} (80%) delete mode 100755 tests/test_cases/language/asi/S7.9_A5.7_T1.js diff --git a/ev.js b/ev.js new file mode 100644 index 00000000..86e5211f --- /dev/null +++ b/ev.js @@ -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= end: assert loc == end - assert len(ctx.stack) == 1 + assert len(ctx.stack) == (1 + initial_len), 'Stack change must be equal to +1!' return ctx.stack.pop(), 0, None # means normal return # execute instruction @@ -106,7 +107,7 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): if len(self.contexts) == entry_level: # check if jumped outside of the fragment and break if so if not start <= loc < end: - assert len(ctx.stack) == 1 + assert len(ctx.stack) == (1+initial_len), 'Stack change must be equal to +1!' return ctx.stack.pop(), 2, status # jump outside continue diff --git a/js2py/internals/constructors/jsfunction.py b/js2py/internals/constructors/jsfunction.py index 9145d19f..bfe631cc 100644 --- a/js2py/internals/constructors/jsfunction.py +++ b/js2py/internals/constructors/jsfunction.py @@ -18,52 +18,52 @@ def Function(this, args): return executable_function(_body, _args, args.space, global_context=True) -# 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 -def executable_function(_body, _args, space, global_context=True): +def executable_function(_body, _args, space, global_context=True): func_str = u'(function (%s) { ; %s ; });' % (u', '.join(_args), _body) - skip = space.byte_generator.exe.get_new_label() - space.byte_generator.emit('JUMP', skip) - space.byte_generator.emit(parse(func_str)['body']) - space.byte_generator.emit('LABEL', skip) - space.byte_generator.emit('NOP') - space.byte_generator.exe.compile() - - ctx = space.GlobalObj if global_context else space.exe.current_ctx - space.byte_generator.exe.tape[-1].eval(ctx) - func = ctx.stack.pop() - return func + 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(parse(code_str)) + space.byte_generator.emit(parsed) space.byte_generator.emit('LABEL', skip) space.byte_generator.emit('NOP') - space.byte_generator.exe.compile() + 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') + 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)) \ No newline at end of file + print ' '.join(map(to_string, args)) + return undefined \ No newline at end of file diff --git a/js2py/internals/fill_space.py b/js2py/internals/fill_space.py index 76c32d97..c5c6faf2 100644 --- a/js2py/internals/fill_space.py +++ b/js2py/internals/fill_space.py @@ -65,6 +65,8 @@ def set_protected(obj, name, prop): def fill_space(space, byte_generator): # set global scope global_scope = Scope({}, space, parent=None) + global_scope.THIS_BINDING = global_scope + global_scope.registers(byte_generator.declared_vars) space.GlobalObj = global_scope space.byte_generator = byte_generator diff --git a/js2py/internals/opcodes.py b/js2py/internals/opcodes.py index cdd58a3e..686bca1b 100644 --- a/js2py/internals/opcodes.py +++ b/js2py/internals/opcodes.py @@ -173,6 +173,8 @@ def eval(self, ctx): class POP(OP_CODE): def eval(self, ctx): + # todo remove this check later + assert len(ctx.stack), 'Popped from empty stack!' del ctx.stack[-1] # class REDUCE(OP_CODE): @@ -308,7 +310,7 @@ def __init__(self, identifier): # 11.1.2 def eval(self, ctx): - ctx.stack.append(ctx.get(self.identifier)) + ctx.stack.append(ctx.get(self.identifier, throw=True)) class LOAD_MEMBER(OP_CODE): @@ -453,7 +455,6 @@ def bytecode_call(ctx, func, this, args): return None # therefore not native. we have to return (new_context, function_label) to instruct interpreter to call - print func.name return func._generate_my_context(this, args), func.code @@ -572,24 +573,24 @@ def eval(self, ctx): # execute_fragment_under_context returns: # (return_value, typ, jump_loc/error) + ctx.stack.pop() + # execute try statement try_status = ctx.space.exe.execute_fragment_under_context(ctx, self.try_label, self.catch_label) - # check if errors errors = try_status[1] == 3 # catch if errors and self.catch_variable is not None: # generate catch block context... catch_context = Scope({self.catch_variable: try_status[2].get_thrown_value(ctx.space)}, ctx.space, ctx) - catch_context.stack.append(undefined) + catch_context.THIS_BINDING = ctx.THIS_BINDING catch_status = ctx.space.exe.execute_fragment_under_context(catch_context, self.catch_label, self.finally_label) else: catch_status = None # finally if self.finally_present: - ctx.stack.append(undefined) # (ctx.stack is empty after try call) finally_status = ctx.space.exe.execute_fragment_under_context(ctx, self.finally_label, self.end_label) else: finally_status = None @@ -613,7 +614,6 @@ def eval(self, ctx): return spec elif typ == 3: # throw is made with empty stack as usual - raise spec else: raise RuntimeError('Invalid return code') @@ -633,6 +633,41 @@ def eval(self, ctx): # ------------ WITH + ITERATORS ---------- +class WITH(OP_CODE): + _params = ['beg_label', 'end_label'] + def __init__(self, beg_label, end_label): + self.beg_label = beg_label + self.end_label = end_label + + def eval(self, ctx): + obj = to_object(ctx.stack.pop(), ctx.space) + + with_context = Scope(obj, ctx.space, ctx) # todo actually use the obj to modify the ctx + with_context.THIS_BINDING = ctx.THIS_BINDING + status = ctx.space.exe.execute_fragment_under_context(with_context, self.beg_label, self.end_label) + + + + val, typ, spec = status + if typ!=3: + print typ + ctx.stack.pop() + + if typ == 0: # normal + ctx.stack.append(val) + return + elif typ == 1: # return + ctx.stack.append(spec) + return None, None # send return signal + elif typ == 2: # jump outside + ctx.stack.append(val) + return spec + elif typ == 3: + # throw is made with empty stack as usual + raise spec + else: + raise RuntimeError('Invalid return code') + # all opcodes... OP_CODES = {} diff --git a/js2py/internals/operations.py b/js2py/internals/operations.py index 03bd5c39..69478e92 100644 --- a/js2py/internals/operations.py +++ b/js2py/internals/operations.py @@ -255,11 +255,11 @@ def instanceof_op(self, other): return other.has_instance(self) -def contains_op(self, other): - '''checks if self contains other''' - if not self.is_object(): +def in_op(self, other): + '''checks if self is in other''' + if not is_object(self): raise MakeError('TypeError', "You can\'t use 'in' operator to search in non-objects") - return self.has_property(to_string(other).value) + return other.has_property(to_string(self)) BINARY_OPERATIONS = { @@ -286,6 +286,7 @@ def contains_op(self, other): '>': greater_op, '>=': greater_eq_op, + 'in': in_op, 'instanceof': instanceof_op, } diff --git a/js2py/internals/prototypes/jsnumber.py b/js2py/internals/prototypes/jsnumber.py index 8943873e..ed6c0f75 100644 --- a/js2py/internals/prototypes/jsnumber.py +++ b/js2py/internals/prototypes/jsnumber.py @@ -31,7 +31,7 @@ class NumberPrototype: def toString(this, args): if GetClass(this)!='Number': raise MakeError('TypeError', 'Number.prototype.valueOf is not generic') - if type(this)!= unicode: + if type(this)!= float: this = this.value radix = get_arg(args, 0) if is_undefined(radix): @@ -64,10 +64,10 @@ def valueOf(this, args): def toFixed (this, args): if GetClass(this)!='Number': raise MakeError('TypeError', 'Number.prototype.toFixed called on incompatible receiver') - if type(this)!= unicode: + if type(this)!= float: this = this.value fractionDigits = get_arg(args, 0) - digs = fractionDigits.to_int() + digs = to_int(fractionDigits) if digs<0 or digs>20: raise MakeError('RangeError', 'toFixed() digits argument must be between 0 and 20') elif is_infinity(this): @@ -80,7 +80,7 @@ def toFixed (this, args): def toExponential(this, args): if GetClass(this)!='Number': raise MakeError('TypeError', 'Number.prototype.toExponential called on incompatible receiver') - if type(this)!= unicode: + if type(this)!= float: this = this.value fractionDigits = get_arg(args, 0) digs = to_int(fractionDigits) @@ -95,12 +95,12 @@ def toExponential(this, args): def toPrecision (this, args): if GetClass(this)!='Number': raise MakeError('TypeError', 'Number.prototype.toPrecision called on incompatible receiver') - if type(this)!= unicode: + if type(this)!= float: this = this.value precision = get_arg(args, 0) if is_undefined(precision): return to_string(this) - prec = precision.to_int() + prec = to_int(precision) if is_nan(this): return 'NaN' elif is_infinity(this): diff --git a/js2py/internals/seval.py b/js2py/internals/seval.py new file mode 100644 index 00000000..2b1430b3 --- /dev/null +++ b/js2py/internals/seval.py @@ -0,0 +1,28 @@ +import pyjsparser +from space import Space +import fill_space +from byte_trans import ByteCodeGenerator +from code import Code +from simplex import MakeError +import sys +sys.setrecursionlimit(100000) + + +pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError(u'SyntaxError', unicode(msg)) + +def eval_js_vm(js): + a = ByteCodeGenerator(Code()) + s = Space() + a.exe.space = s + s.exe = a.exe + + d = pyjsparser.parse(js) + + a.emit(d) + fill_space.fill_space(s, a) + # print a.exe.tape + a.exe.compile() + + return a.exe.run(a.exe.space.GlobalObj) + + diff --git a/js2py/internals/simplex.py b/js2py/internals/simplex.py index 8260e6a8..79f63085 100644 --- a/js2py/internals/simplex.py +++ b/js2py/internals/simplex.py @@ -69,11 +69,16 @@ def is_finite(self): class JsException(Exception): - def __init__(self, typ, message, throw=None): - assert throw is None or (typ is None and message is None), (throw, typ, message) - self.typ = typ - self.message = message - self.throw = throw + def __init__(self, typ=None, message=None, throw=None): + if typ is None and message is None and throw is None: + # it means its the trasnlator based error (old format), do nothing + self._translator_based = True + else: + assert throw is None or (typ is None and message is None), (throw, typ, message) + self._translator_based = False + self.typ = typ + self.message = message + self.throw = throw def get_thrown_value(self, space): if self.throw: @@ -82,11 +87,17 @@ def get_thrown_value(self, space): return space.NewError(self.typ, self.message) def __str__(self): - if self.throw is not None: - from conversions import to_string - return to_string(self.throw) + if self._translator_based: + if self.mes.Class == 'Error': + return self.mes.callprop('toString').value + else: + return unicode(self.mes) else: - return self.typ+': '+self.message + if self.throw is not None: + from conversions import to_string + return to_string(self.throw) + else: + return self.typ+': '+self.message diff --git a/js2py/internals/trans_utils.py b/js2py/internals/trans_utils.py index 3798889b..298fb9c9 100644 --- a/js2py/internals/trans_utils.py +++ b/js2py/internals/trans_utils.py @@ -1,3 +1,4 @@ + def to_key(literal_or_identifier): ''' returns string representation of this object''' if literal_or_identifier['type'] == 'Identifier': diff --git a/js2py/test_internals.py b/js2py/test_internals.py index eade6003..12cf4ad7 100644 --- a/js2py/test_internals.py +++ b/js2py/test_internals.py @@ -1,3 +1,9 @@ from internals import byte_trans +from internals import seval +import pyjsparser -byte_trans.main() \ No newline at end of file +x = r''' +function g() {var h123 = 11; return [function g1() {return h123}, new Function('return h123')]} +g()[1]() +''' +print seval.eval_js_vm(x) diff --git a/js2py/translators/translator.py b/js2py/translators/translator.py index 1b9983c1..9e10b2aa 100644 --- a/js2py/translators/translator.py +++ b/js2py/translators/translator.py @@ -7,7 +7,6 @@ # Enable Js2Py exceptions and pyimport in parser -pyjsparser.parser.ENABLE_JS2PY_ERRORS = True pyjsparser.parser.ENABLE_PYIMPORT = True # the re below is how we'll recognise numeric constants. diff --git a/setup.py b/setup.py index 090e00c5..1aabe1cd 100644 --- a/setup.py +++ b/setup.py @@ -27,12 +27,12 @@ # python setup.py sdist upload -r pypi setup( name='Js2Py', - version='0.45', + version='0.47', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6'], url='https://github.com/PiotrDabkowski/Js2Py', - install_requires = ['tzlocal>=1.2', 'six>=1.10', 'pyjsparser>=2.4.5'], + install_requires = ['tzlocal>=1.2', 'six>=1.10', 'pyjsparser>=2.5.1'], license='MIT', author='Piotr Dabkowski', author_email='piodrus@gmail.com', diff --git a/test_bench.py b/test_bench.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/run_on_js2py.py b/tests/run.py similarity index 80% rename from tests/run_on_js2py.py rename to tests/run.py index 3e084761..5e2030f0 100644 --- a/tests/run_on_js2py.py +++ b/tests/run.py @@ -1,12 +1,42 @@ from __future__ import print_function import js2py from js2py.base import PyJsException, PyExceptionToJs +from js2py.internals import seval +from js2py.internals.simplex import JsException import os, sys, re, traceback, threading, ctypes, time, six from distutils.version import LooseVersion +import codecs +import json +import traceback + +TEST_TIMEOUT = 120 +INCLUDE_PATH = 'includes/' +TEST_PATH = 'test_cases/language/statements' + + +# choose which JS runtime to test. Js2Py has 2 independent runtimes. Translation based (translator) and the vm interpreter based. +RUNTIME_TO_TEST = 'vm' + +if RUNTIME_TO_TEST == 'translator': + JS_EVALUATOR = js2py.eval_js + PY_JS_EXCEPTION = PyJsException + MESSAGE_FROM_PY_JS_EXCEPTION = lambda x: PyExceptionToJs(x).get('message').to_python() +elif RUNTIME_TO_TEST == 'vm': + JS_EVALUATOR = seval.eval_js_vm + PY_JS_EXCEPTION = PyJsException + MESSAGE_FROM_PY_JS_EXCEPTION = lambda x: str(x) +elif RUNTIME_TO_TEST == 'execjs': + import execjs + JS_EVALUATOR = lambda x: execjs.eval('eval(%s)'% json.dumps('{;%s}' % x)) + PY_JS_EXCEPTION = execjs._exceptions.RuntimeError + MESSAGE_FROM_PY_JS_EXCEPTION = lambda x: str(x) +else: + raise RuntimeError("Js2Py has currently only 2 runtimes available - 'translator' and the 'vm' - RUNTIME_TO_TEST must be one of these.") + def load(path): - with open(path, 'r') as f: + with codecs.open(path, "r", "utf-8") as f: return f.read() def terminate_thread(thread): @@ -29,13 +59,11 @@ def terminate_thread(thread): raise SystemError("PyThreadState_SetAsyncExc failed") -TEST_TIMEOUT = 10 -INCLUDE_PATH = 'includes/' -TEST_PATH = 'test_cases/' + INIT = load(os.path.join(INCLUDE_PATH, 'init.js')) -class TestCase: +class FestCase: description = None includes = None author = None @@ -102,7 +130,7 @@ def run(self): crashed = True label = None try: - js2py.eval_js(self.code) + JS_EVALUATOR(self.code) errors = False crashed = False @@ -115,14 +143,14 @@ def run(self): full_error = traceback.format_exc() label = 'NOT_IMPLEMENTED' - except PyJsException as e: + except PY_JS_EXCEPTION as e: crashed = False full_error = traceback.format_exc() if self.negative: passed = True else: passed = False - reason = PyExceptionToJs(e).get('message').to_python() + reason = MESSAGE_FROM_PY_JS_EXCEPTION(e) label = 'FAILED' @@ -138,7 +166,7 @@ def run(self): except: full_error = traceback.format_exc() passed = False - reason = 'UNKNOWN - URGENT, FIX NOW!' + reason = 'UNKNOWN - URGENT, FIX NOW!\n' + traceback.format_exc()+'\n\n' label = 'CRASHED' if not errors: @@ -179,14 +207,14 @@ def list_path(path, folders=False): print('Fuck python 3!') return sorted(res) # python 3 -def test_all(path): +def fest_all(path): files = list_path(path) folders = list_path(path, folders=True) for f in files: if not f.endswith('.js'): continue try: - test = TestCase(f) + test = FestCase(f) if test.strict_only: continue @@ -205,12 +233,12 @@ def test_all(path): except: print(traceback.format_exc()) print(f) - input() + raw_input() for folder in folders: - test_all(folder) + fest_all(folder) -test_all(TEST_PATH) +fest_all(TEST_PATH) diff --git a/tests/test_cases/language/asi/S7.9_A5.7_T1.js b/tests/test_cases/language/asi/S7.9_A5.7_T1.js deleted file mode 100755 index d61fc101..00000000 --- a/tests/test_cases/language/asi/S7.9_A5.7_T1.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - - Since LineTerminator(LT) between Postfix Increment/Decrement - Operator(I/DO) and operand is not allowed, two IO(just as two DO - and their combination) between two references separated by [LT] - after automatic semicolon insertion lead to syntax error - ES6 specifies this as `ReferenceError` - -es5id: 7.9_A5.7_T1 -description: Try use Variable1 \n ++ \n ++ \n Variable2 construction -negative: ReferenceError ----*/ - -var x=0, y=0; -var z= -x -++ -++ -y From 4d969774ed50a61f68a26cc72903158cdde2e5b8 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Thu, 3 Aug 2017 10:21:33 +0200 Subject: [PATCH 02/77] mend --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 1aabe1cd..3c448f09 100644 --- a/setup.py +++ b/setup.py @@ -24,13 +24,14 @@ # python setup.py register -r pypi -# python setup.py sdist upload -r pypi +# python3 setup.py sdist upload -r pypi setup( name='Js2Py', - version='0.47', + version='0.49', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', - 'js2py.constructors', 'js2py.host', 'js2py.es6'], + 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', + 'js2py.internals.prototypes', 'js2py.internals.constructors'], url='https://github.com/PiotrDabkowski/Js2Py', install_requires = ['tzlocal>=1.2', 'six>=1.10', 'pyjsparser>=2.5.1'], license='MIT', From 50f828f6fb405983136fbefb8ed35f571c1a2300 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Fri, 4 Aug 2017 00:08:20 +0200 Subject: [PATCH 03/77] py3 --- js2py/base.py | 2 +- js2py/internals/simplex.py | 6 +++--- setup.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/js2py/base.py b/js2py/base.py index 05e6d63b..5b46e307 100644 --- a/js2py/base.py +++ b/js2py/base.py @@ -981,7 +981,7 @@ def PyJsComma(a, b): return b -from internals.simplex import JsException as PyJsException +from .internals.simplex import JsException as PyJsException import pyjsparser pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError('SyntaxError', msg) diff --git a/js2py/internals/simplex.py b/js2py/internals/simplex.py index 79f63085..3727ebb0 100644 --- a/js2py/internals/simplex.py +++ b/js2py/internals/simplex.py @@ -1,5 +1,5 @@ from __future__ import unicode_literals - +import six #Undefined class PyJsUndefined(object): TYPE = 'Undefined' @@ -17,7 +17,7 @@ class PyJsNull(object): UNDEFINED_TYPE = PyJsUndefined NULL_TYPE = PyJsNull -STRING_TYPE = unicode +STRING_TYPE = unicode if six.PY2 else str NUMBER_TYPE = float BOOLEAN_TYPE = bool @@ -91,7 +91,7 @@ def __str__(self): if self.mes.Class == 'Error': return self.mes.callprop('toString').value else: - return unicode(self.mes) + return self.mes.to_string().value else: if self.throw is not None: from conversions import to_string diff --git a/setup.py b/setup.py index 3c448f09..236d90e3 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # python3 setup.py sdist upload -r pypi setup( name='Js2Py', - version='0.49', + version='0.50', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', From 879b02a6984155efbcf722390b07d077bc23f918 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sat, 14 Oct 2017 21:50:21 +0100 Subject: [PATCH 04/77] Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 74f7e53f..23667bb3 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,17 @@ 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. - +### JavaScript 'VirtualMachine' in Python + +If the translator for some reason does not work for you, you can try the new JS VM: + +```python +from js2py.internals import seval +seval.eval_js_vm(code) +``` + +
+ #### 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. From 47b7c7c264f0a6dd98db5ae675faba2b9df6fb6c Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Wed, 22 Nov 2017 16:29:23 +0000 Subject: [PATCH 05/77] Delete interface.py --- js2py/host/dom/interface.py | 73 ------------------------------------- 1 file changed, 73 deletions(-) delete mode 100644 js2py/host/dom/interface.py diff --git a/js2py/host/dom/interface.py b/js2py/host/dom/interface.py deleted file mode 100644 index 5e3e4a64..00000000 --- a/js2py/host/dom/interface.py +++ /dev/null @@ -1,73 +0,0 @@ -from StringIO import StringIO -from constants import * -from bs4 import BeautifulSoup -from js2py.base import * -try: - import lxml - def parse(source): - return BeautifulSoup(source, 'lxml') -except: - def parse(source): - return BeautifulSoup(source) - - - - - - - -x = ''' - - - - - - - - - - -
Shady GroveAeolian
Over the River, CharlieDorian
''' - - - -class DOM(PyJs): - prototype = ObjectPrototype - def __init__(self): - self.own = {} - - def readonly(self, name, val): - self.define_own_property(name, {'writable':False, 'enumerable':False, 'configurable':False, 'value': Js(val)}) - - - -# DOMStringList - -class DOMStringListPrototype(DOM): - Class = 'DOMStringListPrototype' - - def contains(element): - return element.to_string().value in this._string_list - - def item(index): - return this._string_list[index.to_int()] if 0<=index.to_int() Date: Wed, 22 Nov 2017 16:29:34 +0000 Subject: [PATCH 06/77] Delete constants.py --- js2py/host/dom/constants.py | 47 ------------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 js2py/host/dom/constants.py diff --git a/js2py/host/dom/constants.py b/js2py/host/dom/constants.py deleted file mode 100644 index b4ed209b..00000000 --- a/js2py/host/dom/constants.py +++ /dev/null @@ -1,47 +0,0 @@ -from js2py.base import * - -def _get_conts(idl): - def is_valid(c): - try: - exec(c) - return 1 - except: - pass - return '\n'.join(filter(is_valid, (' '.join(e.strip(' ;').split()[-3:]) for e in idl.splitlines()))) - - -default_attrs = {'writable':True, 'enumerable':True, 'configurable':True} - - -def compose_prototype(Class, attrs=default_attrs): - prototype = Class() - for i in dir(Class): - e = getattr(Class, i) - if hasattr(e, '__func__'): - temp = PyJsFunction(e.__func__, FunctionPrototype) - attrs = {k:v for k,v in attrs.iteritems()} - attrs['value'] = temp - prototype.define_own_property(i, attrs) - return prototype - - -# Error codes - -INDEX_SIZE_ERR = 1 -DOMSTRING_SIZE_ERR = 2 -HIERARCHY_REQUEST_ERR = 3 -WRONG_DOCUMENT_ERR = 4 -INVALID_CHARACTER_ERR = 5 -NO_DATA_ALLOWED_ERR = 6 -NO_MODIFICATION_ALLOWED_ERR = 7 -NOT_FOUND_ERR = 8 -NOT_SUPPORTED_ERR = 9 -INUSE_ATTRIBUTE_ERR = 10 -INVALID_STATE_ERR = 11 -SYNTAX_ERR = 12 -INVALID_MODIFICATION_ERR = 13 -NAMESPACE_ERR = 14 -INVALID_ACCESS_ERR = 15 -VALIDATION_ERR = 16 -TYPE_MISMATCH_ERR = 17 - From 1896022498513c86579566380c35f76acf713010 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Wed, 22 Nov 2017 16:37:49 +0000 Subject: [PATCH 07/77] Create .codeclimate.yml --- .codeclimate.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .codeclimate.yml diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 00000000..225344ab --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,2 @@ +exclude_paths: +- "js2py/es6/babel.py" From 1ac71676bc5f320a596c6618cf2d0a23842f2ca7 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Tue, 12 Dec 2017 21:13:44 +0000 Subject: [PATCH 08/77] added require (allows to import any supported lib from npm) --- .gitignore | 4 ++ js2py/__init__.py | 4 +- js2py/node_import.py | 84 +++++++++++++++++++++++++++++++ js2py/py_node_modules/__init__.py | 1 + setup.py | 4 +- 5 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 js2py/node_import.py create mode 100644 js2py/py_node_modules/__init__.py diff --git a/.gitignore b/.gitignore index a473ab11..4f6e209d 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,7 @@ $RECYCLE.BIN/ .idea/dictionaries/ .idea/inspectionProfiles/ .idea/scopes/ + +js2py/node_modules +js2py/py_node_modules/* +!js2py/py_node_modules/__init__.py \ No newline at end of file diff --git a/js2py/__init__.py b/js2py/__init__.py index 7b71b819..dade6917 100644 --- a/js2py/__init__.py +++ b/js2py/__init__.py @@ -64,8 +64,10 @@ __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'] + '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 diff --git a/js2py/node_import.py b/js2py/node_import.py new file mode 100644 index 00000000..d28e51ef --- /dev/null +++ b/js2py/node_import.py @@ -0,0 +1,84 @@ +__all__ = ['require'] +import subprocess, os, codecs +from .evaljs import translate_js +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('npm link babel-core babel-cli babel-preset-es2015 babelify browserify', 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 require(module_name, update=False): + assert isinstance(module_name, basestring), 'module_name must be a string!' + py_name = module_name.replace('-', '_') + module_filename = '%s.py'%py_name + cached_py_npm_modules = os.listdir(PY_NODE_MODULES_PATH) + if module_filename not in cached_py_npm_modules: + _init() + in_file_name = 'tmp0in439341018923js2py.js' + out_file_name = 'tmp0out439341018923js2py.js' + code = ADD_TO_GLOBALS_FUNC + 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) + + # make sure the module is installed + assert subprocess.call('npm link %s' % module_name, shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + module_name + + # convert the module + assert subprocess.call( + '''browserify %s -o %s -t [ babelify --presets [ es2015 ] ]''' % (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)) + + js_code += GET_FROM_GLOBALS_FUNC + js_code += ';var %s = getFromGlobals(%s);%s' % (py_name, repr(module_name), py_name) + print('Please wait, translating...') + py_code = translate_js(js_code) + + with open(os.path.join(PY_NODE_MODULES_PATH, module_filename), 'wb') as f: + f.write(py_code) + else: + with codecs.open(os.path.join(PY_NODE_MODULES_PATH, module_filename), "r", "utf-8") as f: + py_code = f.read() + + context = {} + exec py_code in context + return context['var'][py_name].to_py() diff --git a/js2py/py_node_modules/__init__.py b/js2py/py_node_modules/__init__.py new file mode 100644 index 00000000..3a6e544e --- /dev/null +++ b/js2py/py_node_modules/__init__.py @@ -0,0 +1 @@ +"""this package contains all the npm modules translated by js2py via node import""" diff --git a/setup.py b/setup.py index 236d90e3..b0bbbf9f 100644 --- a/setup.py +++ b/setup.py @@ -27,11 +27,11 @@ # python3 setup.py sdist upload -r pypi setup( name='Js2Py', - version='0.50', + version='0.51', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', - 'js2py.internals.prototypes', 'js2py.internals.constructors'], + 'js2py.internals.prototypes', 'js2py.internals.constructors', 'js2py.py_node_modules'], url='https://github.com/PiotrDabkowski/Js2Py', install_requires = ['tzlocal>=1.2', 'six>=1.10', 'pyjsparser>=2.5.1'], license='MIT', From d4220c11ab2d053fb57d08bf5c41b23adcfd3c23 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Tue, 12 Dec 2017 21:46:47 +0000 Subject: [PATCH 09/77] small fix --- js2py/__init__.py | 2 +- js2py/node_import.py | 9 +++++---- setup.py | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/js2py/__init__.py b/js2py/__init__.py index dade6917..b6c7bab4 100644 --- a/js2py/__init__.py +++ b/js2py/__init__.py @@ -69,5 +69,5 @@ from .base import PyJsException from .evaljs import * from .translators import parse as parse_js -from node_import import require +from .node_import import require diff --git a/js2py/node_import.py b/js2py/node_import.py index d28e51ef..0a3c0dc9 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -9,7 +9,7 @@ def _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('npm link babel-core babel-cli babel-preset-es2015 babelify browserify', shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules' + assert subprocess.call('cd %s;npm link babel-core babel-cli babel-preset-es2015 babelify browserify' % repr(DIRNAME), shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules' DID_INIT = True ADD_TO_GLOBALS_FUNC = ''' @@ -37,7 +37,7 @@ def _init(): ''' def require(module_name, update=False): - assert isinstance(module_name, basestring), 'module_name must be a string!' + assert isinstance(module_name, str), 'module_name must be a string!' py_name = module_name.replace('-', '_') module_filename = '%s.py'%py_name cached_py_npm_modules = os.listdir(PY_NODE_MODULES_PATH) @@ -54,7 +54,7 @@ def require(module_name, update=False): f.write(code) # make sure the module is installed - assert subprocess.call('npm link %s' % module_name, shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + module_name + assert subprocess.call('cd %s;npm link %s' %(repr(DIRNAME), module_name), shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + module_name # convert the module assert subprocess.call( @@ -80,5 +80,6 @@ def require(module_name, update=False): py_code = f.read() context = {} - exec py_code in context + exec(py_code, context) return context['var'][py_name].to_py() + diff --git a/setup.py b/setup.py index b0bbbf9f..dca2a1a8 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # python3 setup.py sdist upload -r pypi setup( name='Js2Py', - version='0.51', + version='0.52', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', From 0f3d4bde0a5e6e646803a5ae95542e0068c9087c Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Tue, 12 Dec 2017 21:59:46 +0000 Subject: [PATCH 10/77] should work now... --- examples/importing_node_module.py | 8 ++++++++ js2py/node_import.py | 4 ++-- setup.py | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 examples/importing_node_module.py diff --git a/examples/importing_node_module.py b/examples/importing_node_module.py new file mode 100644 index 00000000..9b8c2e7b --- /dev/null +++ b/examples/importing_node_module.py @@ -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)) diff --git a/js2py/node_import.py b/js2py/node_import.py index 0a3c0dc9..98d452a7 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -9,7 +9,7 @@ def _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 link babel-core babel-cli babel-preset-es2015 babelify browserify' % repr(DIRNAME), shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules' + assert subprocess.call('cd %s;npm install babel-core babel-cli babel-preset-es2015 babelify browserify' % repr(DIRNAME), shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules' DID_INIT = True ADD_TO_GLOBALS_FUNC = ''' @@ -54,7 +54,7 @@ def require(module_name, update=False): f.write(code) # make sure the module is installed - assert subprocess.call('cd %s;npm link %s' %(repr(DIRNAME), module_name), shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + module_name + assert subprocess.call('cd %s;npm install %s' %(repr(DIRNAME), module_name), shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + module_name # convert the module assert subprocess.call( diff --git a/setup.py b/setup.py index dca2a1a8..8cd370eb 100644 --- a/setup.py +++ b/setup.py @@ -23,11 +23,11 @@ -# python setup.py register -r pypi -# python3 setup.py sdist upload -r pypi +# python setup.py sdist bdist_wheel +# twine upload dist/* setup( name='Js2Py', - version='0.52', + version='0.53', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', From a5d8f096bb527014dd8405dea7366c9c1fff9530 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Thu, 14 Dec 2017 10:39:38 +0000 Subject: [PATCH 11/77] fixed es5 generation --- .gitignore | 1 + js2py/node_import.py | 10 ++++++---- setup.py | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4f6e209d..f36e4c1b 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,7 @@ $RECYCLE.BIN/ .idea/inspectionProfiles/ .idea/scopes/ +bench.py js2py/node_modules js2py/py_node_modules/* !js2py/py_node_modules/__init__.py \ No newline at end of file diff --git a/js2py/node_import.py b/js2py/node_import.py index 98d452a7..2428aa1c 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -9,7 +9,7 @@ def _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 babelify browserify' % repr(DIRNAME), shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules' + assert subprocess.call('cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify' % repr(DIRNAME), shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules' DID_INIT = True ADD_TO_GLOBALS_FUNC = ''' @@ -36,16 +36,18 @@ def _init(): ''' -def require(module_name, update=False): +def require(module_name, include_polyfill=False, update=False): assert isinstance(module_name, str), 'module_name must be a string!' py_name = module_name.replace('-', '_') module_filename = '%s.py'%py_name cached_py_npm_modules = os.listdir(PY_NODE_MODULES_PATH) - if module_filename not in cached_py_npm_modules: + if module_filename not in cached_py_npm_modules 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); @@ -58,7 +60,7 @@ def require(module_name, update=False): # convert the module assert subprocess.call( - '''browserify %s -o %s -t [ babelify --presets [ es2015 ] ]''' % (in_file_name, out_file_name), + '''node -e "(require('browserify')('./%s').bundle(function (err,data) {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' diff --git a/setup.py b/setup.py index 8cd370eb..a1740c3f 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.53', + version='0.54', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', From c8a86f7a799bdccafd25b0e77da6b5c60c8e1b06 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sat, 16 Dec 2017 22:54:24 +0000 Subject: [PATCH 12/77] added uri functions --- .travis.yml | 2 +- js2py/host/jsfunctions.py | 25 +++++++++++++++++- js2py/node_import.py | 4 +-- js2py/pyjs.py | 11 ++++++-- setup.py | 2 +- simple_test.py | 54 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 90 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index c89fa5fa..792002cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,6 @@ python: - "3.5" - "3.6" # 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 \ No newline at end of file diff --git a/js2py/host/jsfunctions.py b/js2py/host/jsfunctions.py index 592b55c1..f7cd815f 100644 --- a/js2py/host/jsfunctions.py +++ b/js2py/host/jsfunctions.py @@ -1,4 +1,6 @@ from ..base import * +from six.moves.urllib.parse import quote, unquote + RADIX_CHARS = {'1': 1, '0': 0, '3': 3, '2': 2, '5': 5, '4': 4, '7': 7, '6': 6, '9': 9, '8': 8, 'a': 10, 'c': 12, 'b': 11, 'e': 14, 'd': 13, 'g': 16, 'f': 15, 'i': 18, 'h': 17, 'k': 20, 'j': 19, 'm': 22, 'l': 21, @@ -79,7 +81,28 @@ def isFinite(number): return true -#todo URI handling! +# todo test them properly + +@Js +def escape(text): + return quote(text.to_string().value) + +@Js +def unescape(text): + return unquote(text.to_string().value) + +@Js +def encodeURI(text): + return quote(text.to_string().value, safe='~@#$&()*!+=:;,.?/\'') +@Js +def decodeURI(text): + return unquote(text.to_string().value) +@Js +def encodeURIComponent(text): + return quote(text.to_string().value, safe='~()*!.\'') +@Js +def decodeURIComponent(text): + return unquote(text.to_string().value) diff --git a/js2py/node_import.py b/js2py/node_import.py index 2428aa1c..b62c289c 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -53,7 +53,7 @@ def require(module_name, include_polyfill=False, update=False): 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) + f.write(code.encode('utf-8')) # make sure the module is installed assert subprocess.call('cd %s;npm install %s' %(repr(DIRNAME), module_name), shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + module_name @@ -76,7 +76,7 @@ def require(module_name, include_polyfill=False, update=False): py_code = translate_js(js_code) with open(os.path.join(PY_NODE_MODULES_PATH, module_filename), 'wb') as f: - f.write(py_code) + f.write(py_code.encode('utf-8')) else: with codecs.open(os.path.join(PY_NODE_MODULES_PATH, module_filename), "r", "utf-8") as f: py_code = f.read() diff --git a/js2py/pyjs.py b/js2py/pyjs.py index db49f4f7..55b1d606 100644 --- a/js2py/pyjs.py +++ b/js2py/pyjs.py @@ -21,7 +21,9 @@ from .prototypes.jsjson import JSON from .host.console import console from .host.jseval import Eval -from .host.jsfunctions import parseFloat, parseInt, isFinite, isNaN +from .host.jsfunctions import parseFloat, parseInt, isFinite, \ + isNaN, escape, unescape, encodeURI, decodeURI, encodeURIComponent, decodeURIComponent + # Now we have all the necessary items to create global environment for script __all__ = ['Js', 'PyJsComma', 'PyJsStrictEq', 'PyJsStrictNeq', @@ -39,7 +41,9 @@ 'Int32Array', 'Uint32Array', 'Float32Array', 'Float64Array', 'ArrayBuffer', - 'parseFloat', 'parseInt', 'isFinite', 'isNaN') + 'parseFloat', 'parseInt', 'isFinite', 'isNaN', + 'escape', 'unescape', 'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent', + ) #Array, Function, JSON, Error is done later :) # also some built in functions like eval... @@ -52,6 +56,9 @@ def set_global_object(obj): # make this available obj.register('this') obj.put('this', this) + # also add window and set it to be a global object for compatibility + obj.register('window') + obj.put('window', this) diff --git a/setup.py b/setup.py index a1740c3f..4b65342f 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.54', + version='0.57', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', diff --git a/simple_test.py b/simple_test.py index 93a1998d..08c61bc6 100644 --- a/simple_test.py +++ b/simple_test.py @@ -26,7 +26,59 @@ except js2py.PyJsException as err: assert str(err).startswith('SyntaxError: ') -print("Passed ECMA 5!\n"+30*'-') + +print("Passed ECMA 5 simple tests!\n"+30*'-') + + +print('Now harder tests - test on huge JS libraries:') + +# crypto-js ( https://www.npmjs.com/package/crypto-js ) +print('Testing crypto-js') +jsjson = js2py.eval_js('JSON') +CryptoJS = js2py.require('crypto-js') +data = [{'id': 1}, {'id': 2}] +ciphertext = CryptoJS.AES.encrypt(jsjson.stringify(data), 'secret key 123') +wrong_bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'wrong key') +assert not wrong_bytes.toString() +bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'secret key 123') +decryptedData = jsjson.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() +assert decryptedData == data + +# esprima ( https://www.npmjs.com/package/esprima ) +# escodegen ( https://github.com/estools/escodegen ) +print('Testing esprima & escodegen') +esprima = js2py.require('esprima') +escodegen = js2py.require('escodegen') +print('now use the translated esprima to parse some js code!') +sample_js_code = ''' +function helloWorld(a, b, c) { + console.log(this+a+b+c); 8; var x={hh:1,i:0}; + if (a!=1) { + return x + } else { + throw error + } +} +''' +# we can even parse esprima's own source code, but its size is too big for the travis test. +# sample_js_code = js2py.get_file_contents('examples/esprima.js') +parsed = esprima.parse(sample_js_code) +print('use escodegen to get back the js code from the parsed AST') +reconstructed = escodegen.generate(parsed) +parsed2 = esprima.parse(sample_js_code) +reconstructed2 = escodegen.generate(parsed) +assert reconstructed==reconstructed2 and len(reconstructed)>=len(sample_js_code) and reconstructed.startswith('function') + + +# chalk ( https://github.com/chalk/chalk ) +print('For the final test use chalk. this is interesting because it ' + 'uses ES6 objects like Map, Therefore we have to include the polyfills!') +chalk = js2py.require('chalk', include_polyfill=True) +chalk = chalk.constructor.new({'level': 2}) +true_text = '\x1b[34mHello \x1b[4m\x1b[44mworld\x1b[49m\x1b[24m!\x1b[39m' +text = chalk.blue('Hello', chalk.underline.bgBlue('world') + '!') +print(text) +assert text==true_text print("Testing ECMA 6...") From 0a55eefbed6e9691976184bfb258c75730bd6a7f Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sun, 17 Dec 2017 00:40:40 +0000 Subject: [PATCH 13/77] now you can import any node module is if it was written in python! --- README.md | 16 ++++++++++++++++ js2py/node_import.py | 5 +++-- setup.py | 2 +- simple_test.py | 11 ++++++----- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 23667bb3..521ab724 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,23 @@ Simple Example: 6 >>> add.constructor function Function() { [python code] } + >> CryptoJS = js2py.require('crypto-js') ``` +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): diff --git a/js2py/node_import.py b/js2py/node_import.py index b62c289c..942d34bb 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -1,6 +1,7 @@ __all__ = ['require'] import subprocess, os, codecs from .evaljs import translate_js +import six DID_INIT = False DIRNAME = os.path.dirname(os.path.abspath(__file__)) PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'py_node_modules') @@ -53,7 +54,7 @@ def require(module_name, include_polyfill=False, update=False): 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')) + f.write(code.encode('utf-8') if six.PY3 else code) # make sure the module is installed assert subprocess.call('cd %s;npm install %s' %(repr(DIRNAME), module_name), shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + module_name @@ -76,7 +77,7 @@ def require(module_name, include_polyfill=False, update=False): py_code = translate_js(js_code) with open(os.path.join(PY_NODE_MODULES_PATH, module_filename), 'wb') as f: - f.write(py_code.encode('utf-8')) + f.write(py_code.encode('utf-8') if six.PY3 else code) else: with codecs.open(os.path.join(PY_NODE_MODULES_PATH, module_filename), "r", "utf-8") as f: py_code = f.read() diff --git a/setup.py b/setup.py index 4b65342f..bc086d1b 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.57', + version='0.58', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', diff --git a/simple_test.py b/simple_test.py index 08c61bc6..1663595f 100644 --- a/simple_test.py +++ b/simple_test.py @@ -34,16 +34,17 @@ # crypto-js ( https://www.npmjs.com/package/crypto-js ) print('Testing crypto-js') -jsjson = js2py.eval_js('JSON') CryptoJS = js2py.require('crypto-js') data = [{'id': 1}, {'id': 2}] -ciphertext = CryptoJS.AES.encrypt(jsjson.stringify(data), 'secret key 123') -wrong_bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'wrong key') -assert not wrong_bytes.toString() +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 = jsjson.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() +decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() assert decryptedData == data +wrong_bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'wrong key') +assert not wrong_bytes.toString() + # esprima ( https://www.npmjs.com/package/esprima ) # escodegen ( https://github.com/estools/escodegen ) print('Testing esprima & escodegen') From 9c8306274af9b592159434b4f5311d282e90eed9 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sun, 17 Dec 2017 00:44:40 +0000 Subject: [PATCH 14/77] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 521ab724..7e797567 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ Simple Example: 6 >>> add.constructor function Function() { [python code] } - >> CryptoJS = js2py.require('crypto-js') + >>> 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): From 1358295e7327d11cfce9c5146dbfca71a2888cfc Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sun, 17 Dec 2017 00:53:44 +0000 Subject: [PATCH 15/77] node import fix --- js2py/node_import.py | 2 +- simple_test.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/js2py/node_import.py b/js2py/node_import.py index 942d34bb..c679310e 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -77,7 +77,7 @@ def require(module_name, include_polyfill=False, update=False): py_code = translate_js(js_code) 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 code) + 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() diff --git a/simple_test.py b/simple_test.py index 1663595f..8ad00355 100644 --- a/simple_test.py +++ b/simple_test.py @@ -42,9 +42,6 @@ decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() assert decryptedData == data -wrong_bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'wrong key') -assert not wrong_bytes.toString() - # esprima ( https://www.npmjs.com/package/esprima ) # escodegen ( https://github.com/estools/escodegen ) print('Testing esprima & escodegen') From aad00e89421f0ce70f5ad3657f660bc610b3dde3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Thu, 21 Dec 2017 15:46:29 +0100 Subject: [PATCH 16/77] Support RFC3339 dates without microseconds --- js2py/constructors/jsdate.py | 5 ++++- js2py/internals/constructors/jsdate.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/js2py/constructors/jsdate.py b/js2py/constructors/jsdate.py index 67055e2c..d88fbd77 100644 --- a/js2py/constructors/jsdate.py +++ b/js2py/constructors/jsdate.py @@ -96,7 +96,10 @@ def utc_strftime(self, pattern): def parse_date(py_string): # todo support all date string formats try: - dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%S.%fZ") + 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) diff --git a/js2py/internals/constructors/jsdate.py b/js2py/internals/constructors/jsdate.py index 67055e2c..d88fbd77 100644 --- a/js2py/internals/constructors/jsdate.py +++ b/js2py/internals/constructors/jsdate.py @@ -96,7 +96,10 @@ def utc_strftime(self, pattern): def parse_date(py_string): # todo support all date string formats try: - dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%S.%fZ") + 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) From cd3fb435e33b9053d9586663b5535c257dcb2d6f Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 15 Jan 2018 16:33:48 +0800 Subject: [PATCH 17/77] js2py.require support path such as js2py.require('crypto-js/aes') --- js2py/node_import.py | 18 +++++++++++++----- simple_test.py | 8 +++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/js2py/node_import.py b/js2py/node_import.py index c679310e..83985d1f 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -1,5 +1,5 @@ __all__ = ['require'] -import subprocess, os, codecs +import subprocess, os, codecs, glob from .evaljs import translate_js import six DID_INIT = False @@ -41,7 +41,11 @@ def require(module_name, include_polyfill=False, update=False): assert isinstance(module_name, str), 'module_name must be a string!' py_name = module_name.replace('-', '_') module_filename = '%s.py'%py_name - cached_py_npm_modules = os.listdir(PY_NODE_MODULES_PATH) + var_name = py_name.rpartition('/')[-1] + oldcwd = os.getcwd() + os.chdir(PY_NODE_MODULES_PATH) + cached_py_npm_modules = glob.glob('**', recursive=True) + os.chdir(oldcwd) if module_filename not in cached_py_npm_modules or update: _init() in_file_name = 'tmp0in439341018923js2py.js' @@ -56,8 +60,9 @@ def require(module_name, include_polyfill=False, update=False): 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), module_name), shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + module_name + 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( @@ -72,10 +77,13 @@ def require(module_name, include_polyfill=False, update=False): os.remove(os.path.join(DIRNAME, out_file_name)) js_code += GET_FROM_GLOBALS_FUNC - js_code += ';var %s = getFromGlobals(%s);%s' % (py_name, repr(module_name), py_name) + 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: @@ -84,5 +92,5 @@ def require(module_name, include_polyfill=False, update=False): context = {} exec(py_code, context) - return context['var'][py_name].to_py() + return context['var'][var_name].to_py() diff --git a/simple_test.py b/simple_test.py index 8ad00355..c4903a9a 100644 --- a/simple_test.py +++ b/simple_test.py @@ -42,6 +42,13 @@ decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() assert decryptedData == data +AES = js2py.require('crypto-js/aes') +ciphertext = AES.encrypt(JSON.stringify(data), 'secret key 123') +bytes = AES.decrypt(ciphertext.toString(), 'secret key 123') +decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() +assert decryptedData == data + + # esprima ( https://www.npmjs.com/package/esprima ) # escodegen ( https://github.com/estools/escodegen ) print('Testing esprima & escodegen') @@ -116,4 +123,3 @@ class Shape { a = new Shape(1,2,3) ''').x == 2 print("Passed ECMA 6!") - From 6c0d47627d208ebb0dab92e910ccd5c807d5a877 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 15 Jan 2018 16:42:35 +0800 Subject: [PATCH 18/77] remove empty line --- js2py/node_import.py | 1 - 1 file changed, 1 deletion(-) diff --git a/js2py/node_import.py b/js2py/node_import.py index 83985d1f..f9f1a6a2 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -93,4 +93,3 @@ def require(module_name, include_polyfill=False, update=False): context = {} exec(py_code, context) return context['var'][var_name].to_py() - From 21e9ea87394160995217601990bc8a0d93dbb884 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 15 Jan 2018 16:44:04 +0800 Subject: [PATCH 19/77] add empty lin --- js2py/node_import.py | 1 + 1 file changed, 1 insertion(+) diff --git a/js2py/node_import.py b/js2py/node_import.py index f9f1a6a2..83985d1f 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -93,3 +93,4 @@ def require(module_name, include_polyfill=False, update=False): context = {} exec(py_code, context) return context['var'][var_name].to_py() + From aa74bfda8ac1f3181104ffd8835b3f77060c0c8a Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 15 Jan 2018 16:45:18 +0800 Subject: [PATCH 20/77] add empty line --- simple_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/simple_test.py b/simple_test.py index c4903a9a..904dfa3c 100644 --- a/simple_test.py +++ b/simple_test.py @@ -123,3 +123,4 @@ class Shape { a = new Shape(1,2,3) ''').x == 2 print("Passed ECMA 6!") + From f8cdb7dda6e0b29d61817ffc31fd1637b2809524 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 15 Jan 2018 16:56:33 +0800 Subject: [PATCH 21/77] support for py2 --- js2py/node_import.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/js2py/node_import.py b/js2py/node_import.py index 83985d1f..30dc1b60 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -42,11 +42,7 @@ def require(module_name, include_polyfill=False, update=False): py_name = module_name.replace('-', '_') module_filename = '%s.py'%py_name var_name = py_name.rpartition('/')[-1] - oldcwd = os.getcwd() - os.chdir(PY_NODE_MODULES_PATH) - cached_py_npm_modules = glob.glob('**', recursive=True) - os.chdir(oldcwd) - if module_filename not in cached_py_npm_modules or update: + 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' From c393e5e8bcd214d7e62e7edc8f2cf78bab6d3b28 Mon Sep 17 00:00:00 2001 From: Korney Czukowski Date: Fri, 26 Jan 2018 14:28:10 +0100 Subject: [PATCH 22/77] Cast start argument to int --- js2py/internals/prototypes/jsstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js2py/internals/prototypes/jsstring.py b/js2py/internals/prototypes/jsstring.py index d3f37d0b..81d5acc1 100644 --- a/js2py/internals/prototypes/jsstring.py +++ b/js2py/internals/prototypes/jsstring.py @@ -259,7 +259,7 @@ def split(this, args): def substring (this, args): cok(this) s = to_string(this) - start = get_arg(args, 0) + 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 1898e9eae9d2599d21e0f1f452551b8e5cc85d5a Mon Sep 17 00:00:00 2001 From: Korney Czukowski Date: Fri, 26 Jan 2018 15:20:27 +0100 Subject: [PATCH 23/77] Check for object has been applied to wrong argument --- js2py/internals/operations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js2py/internals/operations.py b/js2py/internals/operations.py index 69478e92..3bbda03d 100644 --- a/js2py/internals/operations.py +++ b/js2py/internals/operations.py @@ -257,7 +257,7 @@ def instanceof_op(self, other): def in_op(self, other): '''checks if self is in other''' - if not is_object(self): + if not is_object(other): raise MakeError('TypeError', "You can\'t use 'in' operator to search in non-objects") return other.has_property(to_string(self)) From b1bbbeb0bcfef6f9548217f51caa4545545de550 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sun, 28 Jan 2018 23:11:28 +0000 Subject: [PATCH 24/77] fixes #111 --- js2py/internals/base.py | 4 +++- js2py/internals/byte_trans.py | 2 +- js2py/internals/constructors/jsconsole.py | 10 ++++++++++ js2py/internals/fill_space.py | 10 ++++++++-- js2py/internals/opcodes.py | 8 +++----- 5 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 js2py/internals/constructors/jsconsole.py diff --git a/js2py/internals/base.py b/js2py/internals/base.py index c040a47c..559d6cbc 100644 --- a/js2py/internals/base.py +++ b/js2py/internals/base.py @@ -457,7 +457,7 @@ def __init__(self, body, flags, prototype=None): reg = reg.replace(fix, rep) # print 'Fix', fix, '->', rep, '=', reg else: - raise + raise Exception() REGEXP_DB[body, flags] = self.pat except: #print 'Invalid pattern...', self.value, comp @@ -594,6 +594,8 @@ def put(self, var, val, throw=False): if self.is_with_scope: if self.own.has_property(var): return self.own.put(var, val, throw=throw) + else: + return self.prototype.put(var, val) # trying to put in local scope # we dont know yet in which scope we should place this var elif var in self.own: diff --git a/js2py/internals/byte_trans.py b/js2py/internals/byte_trans.py index eb042fba..de777cc4 100644 --- a/js2py/internals/byte_trans.py +++ b/js2py/internals/byte_trans.py @@ -613,8 +613,8 @@ def WithStatement(self, object, body, **kwargs): self.emit('LABEL', beg_label) self.emit('LOAD_UNDEFINED') self.emit(body) - self.emit('LABEL', end_label) self.emit('NOP') + self.emit('LABEL', end_label) # with statement implementation self.emit('WITH', beg_label, end_label) diff --git a/js2py/internals/constructors/jsconsole.py b/js2py/internals/constructors/jsconsole.py new file mode 100644 index 00000000..0e6d29f0 --- /dev/null +++ b/js2py/internals/constructors/jsconsole.py @@ -0,0 +1,10 @@ +from __future__ import unicode_literals + +from js2py.internals.conversions import * +from js2py.internals.func_utils import * + + +class ConsoleMethods: + def log(this, args): + x = ' '.join(to_string(e) for e in args) + print(x) diff --git a/js2py/internals/fill_space.py b/js2py/internals/fill_space.py index c5c6faf2..13708a62 100644 --- a/js2py/internals/fill_space.py +++ b/js2py/internals/fill_space.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from base import Scope from func_utils import * from conversions import * @@ -13,7 +15,6 @@ import prototypes.jsjson as jsjson import prototypes.jsutils as jsutils - from constructors import jsnumber from constructors import jsstring from constructors import jsarray @@ -22,6 +23,8 @@ from constructors import jsmath from constructors import jsobject from constructors import jsfunction +from constructors import jsconsole + def fill_proto(proto, proto_class, space): for i in dir(proto_class): @@ -244,6 +247,8 @@ def new_create(args, space): for k,v in jsmath.CONSTANTS.items(): set_protected(math, k, v) + console = space.NewObject() + fill_proto(console, jsconsole.ConsoleMethods, space) # set global object @@ -264,7 +269,8 @@ def new_create(args, space): 'isFinite': isFinite, 'isNaN': isNaN, 'eval': easy_func(jsfunction._eval, space), - 'log': easy_func(jsfunction.log, space), + 'console': console, + 'log': console.get(u'log'), } builtins.update(error_constructors) diff --git a/js2py/internals/opcodes.py b/js2py/internals/opcodes.py index 686bca1b..f46c7791 100644 --- a/js2py/internals/opcodes.py +++ b/js2py/internals/opcodes.py @@ -646,11 +646,9 @@ def eval(self, ctx): with_context.THIS_BINDING = ctx.THIS_BINDING status = ctx.space.exe.execute_fragment_under_context(with_context, self.beg_label, self.end_label) - - val, typ, spec = status - if typ!=3: - print typ + + if typ != 3: # exception ctx.stack.pop() if typ == 0: # normal @@ -662,7 +660,7 @@ def eval(self, ctx): elif typ == 2: # jump outside ctx.stack.append(val) return spec - elif typ == 3: + elif typ == 3: # exception # throw is made with empty stack as usual raise spec else: From a0d33699ce518e9edcaa0db22641729a9db91aac Mon Sep 17 00:00:00 2001 From: Korney Czukowski Date: Sun, 4 Feb 2018 13:20:20 +0100 Subject: [PATCH 25/77] Swap binary op arguments in STORE_MEMBER_OP and STORE_MEMBER_DOT_OP --- js2py/internals/opcodes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js2py/internals/opcodes.py b/js2py/internals/opcodes.py index f46c7791..1aa748ab 100644 --- a/js2py/internals/opcodes.py +++ b/js2py/internals/opcodes.py @@ -415,10 +415,10 @@ def eval(self, ctx): raise MakeError('TypeError', "Cannot set property '%s' of null" % to_string(name)) elif typ is UNDEFINED_TYPE: raise MakeError('TypeError', "Cannot set property '%s' of undefined" % to_string(name)) - ctx.stack.append(BINARY_OPERATIONS[self.op](value, get_member(left, name, ctx.space))) + ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(left, name, ctx.space), value)) return else: - ctx.stack.append(BINARY_OPERATIONS[self.op](value, get_member(left, name, ctx.space))) + ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(left, name, ctx.space), value)) left.put_member(name, ctx.stack[-1]) @@ -438,10 +438,10 @@ def eval(self, ctx): raise MakeError('TypeError', "Cannot set property '%s' of null" % self.prop) elif typ == UNDEFINED_TYPE: raise MakeError('TypeError', "Cannot set property '%s' of undefined" % self.prop) - ctx.stack.append(BINARY_OPERATIONS[self.op](value, get_member_dot(left, self.prop, ctx.space))) + ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(left, self.prop, ctx.space), value)) return else: - ctx.stack.append(BINARY_OPERATIONS[self.op](value, get_member_dot(left, self.prop, ctx.space))) + ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(left, self.prop, ctx.space), value)) left.put(self.prop, ctx.stack[-1]) From 8a2edccf9dc38a2538936ec8fd3178c6ba17c069 Mon Sep 17 00:00:00 2001 From: Korney Czukowski Date: Sun, 4 Feb 2018 13:21:59 +0100 Subject: [PATCH 26/77] Add space argument to recursive convert_to_js_type calls --- js2py/internals/func_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js2py/internals/func_utils.py b/js2py/internals/func_utils.py index bf22cfa7..eae1e778 100644 --- a/js2py/internals/func_utils.py +++ b/js2py/internals/func_utils.py @@ -35,7 +35,7 @@ def convert_to_js_type(e, space=None): raise MakeError('TypeError', 'Actually an internal error, could not convert to js type because space not specified') new = {} for k,v in e.items(): - new[to_string(convert_to_js_type(k))] = convert_to_js_type(v) + new[to_string(convert_to_js_type(k, space))] = convert_to_js_type(v, space) return space.ConstructObject(new) else: raise MakeError('TypeError', 'Could not convert to js type!') From af268c45a31ac8b952cb064e0105fc6d87c6d951 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Mon, 5 Feb 2018 00:47:28 +0000 Subject: [PATCH 27/77] vm fixes and for-in implementation --- js2py/internals/base.py | 8 +- js2py/internals/byte_trans.py | 39 +++++++- js2py/internals/code.py | 9 +- js2py/internals/constructors/jsconsole.py | 1 + js2py/internals/constructors/jsfunction.py | 1 + js2py/internals/constructors/jsmath.py | 8 +- js2py/internals/constructors/jsnumber.py | 2 +- js2py/internals/constructors/jsobject.py | 8 +- js2py/internals/conversions.py | 1 + js2py/internals/fill_space.py | 2 +- js2py/internals/opcodes.py | 53 +++++++++++ js2py/internals/prototypes/jsarray.py | 95 ++++++++++--------- js2py/internals/prototypes/jsnumber.py | 4 +- js2py/internals/prototypes/jsregexp.py | 2 +- js2py/internals/prototypes/jsstring.py | 6 +- js2py/internals/simplex.py | 2 +- tests/run.py | 8 +- ...y.prototype.find_callable-arrowfunction.js | 13 --- .../built-ins/RegExp/S15.10.2.10_A2.1_T1.js | 24 ----- .../built-ins/RegExp/S15.10.2.10_A2.1_T2.js | 23 ----- .../built-ins/RegExp/S15.10.2.9_A1_T4.js | 22 ----- 21 files changed, 176 insertions(+), 155 deletions(-) delete mode 100755 tests/test_cases/built-ins/Array/prototype/find/Array.prototype.find_callable-arrowfunction.js delete mode 100755 tests/test_cases/built-ins/RegExp/S15.10.2.10_A2.1_T1.js delete mode 100755 tests/test_cases/built-ins/RegExp/S15.10.2.10_A2.1_T2.js delete mode 100755 tests/test_cases/built-ins/RegExp/S15.10.2.9_A1_T4.js diff --git a/js2py/internals/base.py b/js2py/internals/base.py index 559d6cbc..9b33c6f1 100644 --- a/js2py/internals/base.py +++ b/js2py/internals/base.py @@ -84,7 +84,7 @@ def put(self, prop, val, throw=False): 'enumerable': True} def can_put(self, prop): # to check - assert type(prop) == unicode + assert type(prop) == unicode, type(prop) # takes py returns py desc = self.get_own_property(prop) if desc: # if we have this property @@ -98,7 +98,7 @@ def can_put(self, prop): # to check if inherited is None: return self.extensible if is_accessor_descriptor(inherited): - return not inherited['set'].is_undefined() + return not is_undefined(inherited['set']) elif self.extensible: return inherited['writable'] # weird... return False @@ -226,7 +226,7 @@ def get_member(self, prop, space): # general member getter, prop has to be uncon if typ not in PRIMITIVES: # most likely getter for object return self.get_member(prop) # <- object can implement this to support faster prop getting. ex array. elif typ == unicode: # then probably a String - if type(prop)==float: + if type(prop)==float and is_finite(prop): index = int(prop) if index==prop and 0 <= index < len(self): return self[index] @@ -467,7 +467,7 @@ def __init__(self, body, flags, prototype=None): 'global' : {'value': self.glob, 'enumerable': False, 'writable': False, 'configurable': False}, 'ignoreCase' : {'value': bool(self.ignore_case), 'enumerable': False, 'writable': False, 'configurable': False}, 'multiline' : {'value': bool(self.multiline), 'enumerable': False, 'writable': False, 'configurable': False}, - 'lastIndex' : {'value': 0, 'enumerable': False, 'writable': True, 'configurable': False}} + 'lastIndex' : {'value': 0., 'enumerable': False, 'writable': True, 'configurable': False}} def match(self, string, pos): '''string is of course a py string''' diff --git a/js2py/internals/byte_trans.py b/js2py/internals/byte_trans.py index de777cc4..bfb0ec97 100644 --- a/js2py/internals/byte_trans.py +++ b/js2py/internals/byte_trans.py @@ -226,10 +226,45 @@ def ForStatement(self, init, test, update, body, **kwargs): self.emit('JUMP', continue_label) # loop back self.emit('LABEL', break_label) + def ForInStatement(self, left, right, body, **kwargs): - raise NotImplementedError('Sorry FOR-IN not available yet!') + # prepare the needed labels + body_start_label = self.exe.get_new_label() + continue_label = self.exe.get_new_label() + break_label = self.exe.get_new_label() + + # prepare the name + if left['type'] == 'VariableDeclaration': + if len(left['declarations']) != 1: + raise MakeError('SyntaxError', ' Invalid left-hand side in for-in loop: Must have a single binding.') + self.emit(left) + name = left['declarations'][0]['id']['name'] + elif left['type']=='Identifier': + name = left['name'] + else: + raise MakeError('SyntaxError', 'Invalid left-hand side in for-loop') + # prepare the iterable + self.emit(right) + + # emit ForIn Opcode + self.emit('FOR_IN', name, body_start_label, continue_label, break_label) + + # a special continue position + self.emit('LABEL', continue_label) + self.emit('NOP') + + self.emit('LABEL', body_start_label) + self.implicit_continues.append(continue_label) + self.implicit_breaks.append(break_label) + self.emit('LOAD_UNDEFINED') + self.emit(body) + self.implicit_continues.pop() + self.implicit_breaks.pop() + self.emit('NOP') + self.emit('LABEL', break_label) + self.emit('NOP') def FunctionDeclaration(self, id, params, defaults, body, **kwargs): if defaults: @@ -573,7 +608,7 @@ def LexicalDeclaration(self, declarations, kind, **kwargs): def VariableDeclarator(self, id, init, **kwargs): name = id['name'] - if name in SPECIAL_IDENTIFIERS: + if name in SPECIAL_IDENTIFIERS: raise MakeError('Invalid left-hand side in assignment') self.declared_vars.append(name) if init is not None: diff --git a/js2py/internals/code.py b/js2py/internals/code.py index 8e269b98..77f3962d 100644 --- a/js2py/internals/code.py +++ b/js2py/internals/code.py @@ -79,7 +79,10 @@ def execute_fragment_under_context(self, ctx, start_label, end_label): self.current_ctx = ctx return self._execute_fragment_under_context(ctx, start_label, end_label) except JsException as err: - del ctx.stack[:] # undo the things that were put on the stack (if any) + # undo the things that were put on the stack (if any) + # don't worry, I know the recovery is possible through try statement and for this reason try statement + # has its own context and stack so it will not delete the contents of the outer stack + del ctx.stack[:] return undefined, 3, err finally: self.current_ctx = old_curr_ctx @@ -89,6 +92,8 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): initial_len = len(ctx.stack) loc = start entry_level = len(self.contexts) + # for e in self.tape[start:end]: + # print e while loc < len(self.tape): #print loc, self.tape[loc] @@ -126,7 +131,7 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): # return: (None, None) else: if len(self.contexts) == entry_level: - assert len(ctx.stack) == 1 + assert len(ctx.stack) == 1 + initial_len return undefined, 1, ctx.stack.pop() # return signal return_value = ctx.stack.pop() ctx = self.contexts.pop() diff --git a/js2py/internals/constructors/jsconsole.py b/js2py/internals/constructors/jsconsole.py index 0e6d29f0..6840ba70 100644 --- a/js2py/internals/constructors/jsconsole.py +++ b/js2py/internals/constructors/jsconsole.py @@ -8,3 +8,4 @@ class ConsoleMethods: def log(this, args): x = ' '.join(to_string(e) for e in args) print(x) + return undefined diff --git a/js2py/internals/constructors/jsfunction.py b/js2py/internals/constructors/jsfunction.py index bfe631cc..29f81b20 100644 --- a/js2py/internals/constructors/jsfunction.py +++ b/js2py/internals/constructors/jsfunction.py @@ -40,6 +40,7 @@ def executable_code(code_str, space, global_context=True): 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() diff --git a/js2py/internals/constructors/jsmath.py b/js2py/internals/constructors/jsmath.py index a6d6b459..467141ff 100644 --- a/js2py/internals/constructors/jsmath.py +++ b/js2py/internals/constructors/jsmath.py @@ -85,21 +85,21 @@ def round(this, args): def sin(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if not is_finite(a): # it must be a nan return NaN return math.sin(a) def cos(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if not is_finite(a): # it must be a nan return NaN - return math.cos(a) + return math.cos(a) def tan(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if not is_finite(a): # it must be a nan return NaN return math.tan(a) diff --git a/js2py/internals/constructors/jsnumber.py b/js2py/internals/constructors/jsnumber.py index c915f499..fa51d64e 100644 --- a/js2py/internals/constructors/jsnumber.py +++ b/js2py/internals/constructors/jsnumber.py @@ -15,7 +15,7 @@ def NumberConstructor(args, space): temp = space.NewObject() temp.prototype = space.NumberPrototype temp.Class = 'Number' - temp.value = to_number(get_arg(args, 0)) if len(args)>0 else 0. + temp.value = float(to_number(get_arg(args, 0)) if len(args)>0 else 0.) return temp diff --git a/js2py/internals/constructors/jsobject.py b/js2py/internals/constructors/jsobject.py index 37f046ce..c683e57c 100644 --- a/js2py/internals/constructors/jsobject.py +++ b/js2py/internals/constructors/jsobject.py @@ -7,7 +7,7 @@ def Object(this, args): val = get_arg(args, 0) - if val.is_null() or val.is_undefined(): + if is_null(val) or is_undefined(val): return args.space.NewObject() return to_object(val, args.space) @@ -16,7 +16,7 @@ def ObjectCreate(args, space): if len(args): val = get_arg(args, 0) if is_object(val): - #Implementation dependent, but my will simply return :) + # Implementation dependent, but my will simply return :) return val elif type(val) in (NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE): return to_object(val, space) @@ -37,7 +37,7 @@ def getOwnPropertyDescriptor(this, args): if not is_object(obj): raise MakeError('TypeError', 'Object.getOwnPropertyDescriptor called on non-object') desc = obj.own.get(to_string(prop)) - return convert_to_js_type(desc) + return convert_to_js_type(desc, args.space) def getOwnPropertyNames(this, args): @@ -82,7 +82,7 @@ def defineProperties(this, args): if not v.get('enumerable'): continue desc = ToPropertyDescriptor(props.get(unicode(k))) - if not obj.define_own_property(unicode(k), desc): + if not obj.define_own_property(unicode(k), desc, False): raise MakeError('TypeError', 'Failed to define own property: %s'% k) return obj diff --git a/js2py/internals/conversions.py b/js2py/internals/conversions.py index 3732a3a6..6d59f30d 100644 --- a/js2py/internals/conversions.py +++ b/js2py/internals/conversions.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals # Type Conversions. to_type. All must return PyJs subclass instance from simplex import * diff --git a/js2py/internals/fill_space.py b/js2py/internals/fill_space.py index 13708a62..a3058137 100644 --- a/js2py/internals/fill_space.py +++ b/js2py/internals/fill_space.py @@ -92,7 +92,7 @@ def fill_space(space, byte_generator): number_proto = space.NewObject() number_proto.prototype = object_proto fill_proto(number_proto, NumberPrototype, space) - number_proto.value = 0 + number_proto.value = 0. number_proto.Class = 'Number' space.NumberPrototype = number_proto diff --git a/js2py/internals/opcodes.py b/js2py/internals/opcodes.py index 1aa748ab..c39dcc70 100644 --- a/js2py/internals/opcodes.py +++ b/js2py/internals/opcodes.py @@ -667,6 +667,59 @@ def eval(self, ctx): raise RuntimeError('Invalid return code') +class FOR_IN(OP_CODE): + _params = ['name', 'body_start_label', 'continue_label', 'break_label'] + def __init__(self, name, body_start_label, continue_label, break_label): + self.name = name + self.body_start_label = body_start_label + self.continue_label = continue_label + self.break_label = break_label + + def eval(self, ctx): + iterable = ctx.stack.pop() + if is_null(iterable) or is_undefined(iterable): + ctx.stack.pop() + ctx.stack.append(undefined) + return self.break_label + + obj = to_object(iterable, ctx.space) + + for e in sorted(obj.own): + if not obj.own[e]['enumerable']: + continue + + ctx.put(self.name, e) # JS would have been so much nicer if this was ctx.space.put(self.name, obj.get(e)) + + # evaluate the body + status = ctx.space.exe.execute_fragment_under_context(ctx, self.body_start_label, self.break_label) + + val, typ, spec = status + + if typ != 3: # exception + ctx.stack.pop() + + if typ == 0: # normal + ctx.stack.append(val) + continue + elif typ == 1: # return + ctx.stack.append(spec) + return None, None # send return signal + elif typ == 2: # jump outside + # now have to figure out whether this is a continue or something else... + ctx.stack.append(val) + if spec == self.continue_label: + # just a continue, perform next iteration as normal + continue + return spec # break or smth, go there and finish the iteration + elif typ == 3: # exception + # throw is made with empty stack as usual + raise spec + else: + raise RuntimeError('Invalid return code') + + return self.break_label + + # all opcodes... OP_CODES = {} g = '' diff --git a/js2py/internals/prototypes/jsarray.py b/js2py/internals/prototypes/jsarray.py index 1c72cb5a..bef77ab0 100644 --- a/js2py/internals/prototypes/jsarray.py +++ b/js2py/internals/prototypes/jsarray.py @@ -22,20 +22,20 @@ def toString(this, args): def toLocaleString(this, args): array = to_object(this, args.space) - arr_len = to_uint32(array.get('length')) + arr_len = js_arr_length(array) # separator is simply a comma ',' if not arr_len: return '' res = [] for i in xrange(arr_len): - element = array[str(i)] + element = array.get(unicode(i)) if is_undefined(element) or is_null(element): res.append('') else: cand = to_object(element, args.space) str_func = cand.get('toLocaleString') if not is_callable(str_func): - raise MakeError('TypeError', 'toLocaleString method of item at index %d is not callable'%i) + raise MakeError('TypeError', 'toLocaleString method of item at index %d is not callable' % i) res.append(to_string(str_func.call(cand, ()))) return ','.join(res) @@ -76,10 +76,10 @@ def join(this, args): def pop(this, args): #todo check array = to_object(this, args.space) - arr_len = to_uint32(array.get('length')) + arr_len = js_arr_length(array) if not arr_len: array.put('length', float(arr_len)) - return None + return undefined ind = unicode(arr_len-1) element = array.get(ind) array.delete(ind) @@ -95,7 +95,7 @@ def push(this, args): for i, e in enumerate(to_put, arr_len): array.put(unicode(i), e, True) array.put('length', float(arr_len+len(to_put)), True) - return i + return float(i) def reverse(this, args): @@ -117,7 +117,7 @@ def shift(this, args): arr_len = js_arr_length(array) if not arr_len: array.put('length', 0.) - return None + return undefined first = array.get('0') for k in xrange(1, arr_len): from_s, to_s = unicode(k), unicode(k-1) @@ -151,7 +151,7 @@ def slice(this, args): # todo check def sort(this, args): # todo: this assumes array continous (not sparse) - fix for sparse arrays cmpfn = get_arg(args, 0) if not GetClass(this) in ('Array', 'Arguments'): - return to_object(this) # do nothing + return to_object(this, args.space) # do nothing arr_len = js_arr_length(this) if not arr_len: return this @@ -176,22 +176,22 @@ def splice(this, args): array = to_object(this, args.space) start = get_arg(args, 0) deleteCount = get_arg(args, 1) - arr_len = array.get('length').to_uint32() - relative_start = start.to_int() + arr_len = js_arr_length(this) + relative_start = to_int(start) actual_start = max((arr_len + relative_start),0) if relative_start<0 else min(relative_start, arr_len) - actual_delete_count = min(max(deleteCount.to_int(),0 ), arr_len - actual_start) + actual_delete_count = min(max(to_int(deleteCount), 0), arr_len - actual_start) k = 0 - A = this.Js([]) + A = args.space.NewArray(0) # 9 while k1: # initial value present - accumulator = arguments[1] + accumulator = undefined + if len(args) > 1: # initial value present + accumulator = args[1] else: kPresent = False while not kPresent and k1: # initial value present - accumulator = arguments[1] + accumulator = undefined + + if len(args) > 1: # initial value present + accumulator = args[1] else: kPresent = False while not kPresent and k>=0: @@ -425,11 +428,11 @@ def reduceRight(this, args): accumulator = array.get(unicode(k)) k -= 1 if not kPresent: - raise this.MakeError('TypeError', 'Reduce of empty array with no initial value') + raise MakeError('TypeError', 'Reduce of empty array with no initial value') while k>=0: if array.has_property(unicode(k)): kValue = array.get(unicode(k)) - accumulator = callbackfn.call(this.undefined, (accumulator, kValue, this.Js(k), array)) + accumulator = callbackfn.call(undefined, (accumulator, kValue, float(k), array)) k -= 1 return accumulator diff --git a/js2py/internals/prototypes/jsnumber.py b/js2py/internals/prototypes/jsnumber.py index ed6c0f75..05d1fa2e 100644 --- a/js2py/internals/prototypes/jsnumber.py +++ b/js2py/internals/prototypes/jsnumber.py @@ -36,7 +36,7 @@ def toString(this, args): radix = get_arg(args, 0) if is_undefined(radix): return to_str_rep(this) - r = radix.to_int() + r = to_int(radix) if r==10: return to_str_rep(this) if r not in xrange(2, 37) or radix!=r: @@ -57,7 +57,7 @@ def toString(this, args): def valueOf(this, args): if GetClass(this)!='Number': raise MakeError('TypeError', 'Number.prototype.valueOf is not generic') - if type(this)!= unicode: + if type(this)!=float: this = this.value return this diff --git a/js2py/internals/prototypes/jsregexp.py b/js2py/internals/prototypes/jsregexp.py index dd814577..827740e8 100644 --- a/js2py/internals/prototypes/jsregexp.py +++ b/js2py/internals/prototypes/jsregexp.py @@ -34,7 +34,7 @@ def _exec(this, args): # will be changed to exec in base.py. cant name it exec def RegExpExec(this, string, space): if GetClass(this)!='RegExp': - raise this.MakeError('TypeError', 'RegExp.prototype.exec is not generic!') + raise MakeError('TypeError', 'RegExp.prototype.exec is not generic!') string = to_string(string) length = len(string) i = to_int(this.get('lastIndex')) if this.glob else 0 diff --git a/js2py/internals/prototypes/jsstring.py b/js2py/internals/prototypes/jsstring.py index 81d5acc1..b47fb669 100644 --- a/js2py/internals/prototypes/jsstring.py +++ b/js2py/internals/prototypes/jsstring.py @@ -56,7 +56,7 @@ def replacement_template(rep, source, span, npar): class StringPrototype: def toString(this, args): if GetClass(this)!='String': - raise this.MakeError('TypeError', 'String.prototype.toString is not generic') + raise MakeError('TypeError', 'String.prototype.toString is not generic') if type(this)==unicode: return this assert type(this.value) == unicode @@ -64,7 +64,7 @@ def toString(this, args): def valueOf(this, args): if GetClass(this)!='String': - raise this.MakeError('TypeError', 'String.prototype.valueOf is not generic') + raise MakeError('TypeError', 'String.prototype.valueOf is not generic') if type(this)==unicode: return this assert type(this.value) == unicode @@ -122,7 +122,7 @@ def match(this, args): 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', this.Js(0)) + r.put('lastIndex', float(0)) found = [] previous_last_index = 0 last_match = True diff --git a/js2py/internals/simplex.py b/js2py/internals/simplex.py index 3727ebb0..916611dc 100644 --- a/js2py/internals/simplex.py +++ b/js2py/internals/simplex.py @@ -81,7 +81,7 @@ def __init__(self, typ=None, message=None, throw=None): self.throw = throw def get_thrown_value(self, space): - if self.throw: + if self.throw is not None: return self.throw else: return space.NewError(self.typ, self.message) diff --git a/tests/run.py b/tests/run.py index 5e2030f0..5fb42916 100644 --- a/tests/run.py +++ b/tests/run.py @@ -9,9 +9,9 @@ import json import traceback -TEST_TIMEOUT = 120 +TEST_TIMEOUT = 2 INCLUDE_PATH = 'includes/' -TEST_PATH = 'test_cases/language/statements' +TEST_PATH = 'test_cases/language/arguments-object/' # choose which JS runtime to test. Js2Py has 2 independent runtimes. Translation based (translator) and the vm interpreter based. @@ -93,6 +93,7 @@ def __init__(self, path): self.strict_only = False self.code = self.init + self.raw + print(self.code) def _parse_test_info(self): self.raw_info = re.search('/\*---(.+)---\*/', self.raw, re.DOTALL).groups()[0].strip() @@ -103,6 +104,8 @@ def _parse_test_info(self): if category is None: raise RuntimeError('Could not parse test case info! %s' % self.path) category_content += '\n' + line.lstrip() + elif line == 'onlyStrict': + self.strict_only = True else: if category is not None: content = category_content.strip() @@ -211,6 +214,7 @@ def fest_all(path): files = list_path(path) folders = list_path(path, folders=True) for f in files: + print(f) if not f.endswith('.js'): continue try: diff --git a/tests/test_cases/built-ins/Array/prototype/find/Array.prototype.find_callable-arrowfunction.js b/tests/test_cases/built-ins/Array/prototype/find/Array.prototype.find_callable-arrowfunction.js deleted file mode 100755 index 4f63feb5..00000000 --- a/tests/test_cases/built-ins/Array/prototype/find/Array.prototype.find_callable-arrowfunction.js +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2014 Matthew Meyers. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -description: > - Array.prototype.find shouldn't throw a TypeError if - IsCallable(predicate) is true; arrow functions are callable -features: [Array#find, arrow-function] ----*/ - - -[].find(x => x); - diff --git a/tests/test_cases/built-ins/RegExp/S15.10.2.10_A2.1_T1.js b/tests/test_cases/built-ins/RegExp/S15.10.2.10_A2.1_T1.js deleted file mode 100755 index 4fd4b446..00000000 --- a/tests/test_cases/built-ins/RegExp/S15.10.2.10_A2.1_T1.js +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: "CharacterEscape :: c ControlLetter" -es5id: 15.10.2.10_A2.1_T1 -description: "ControlLetter :: A - Z" -onlyStrict ----*/ - -//CHECK#0041-005A -var result = true; -for (alpha = 0x0041; alpha <= 0x005A; alpha++) { - str = String.fromCharCode(alpha % 32); - arr = (new RegExp("\\c" + String.fromCharCode(alpha))).exec(str); - console.log(arr) - if ((arr === null) || (arr[0] !== str)) { - result = false; - } -} - -if (result !== true) { - $ERROR('#1: CharacterEscape :: c A - Z'); -} diff --git a/tests/test_cases/built-ins/RegExp/S15.10.2.10_A2.1_T2.js b/tests/test_cases/built-ins/RegExp/S15.10.2.10_A2.1_T2.js deleted file mode 100755 index 3e4ac6e9..00000000 --- a/tests/test_cases/built-ins/RegExp/S15.10.2.10_A2.1_T2.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: "CharacterEscape :: c ControlLetter" -es5id: 15.10.2.10_A2.1_T2 -description: "ControlLetter :: a - z" -onlyStrict ----*/ - -//CHECK#0061-007A -var result = true; -for (alpha = 0x0061; alpha <= 0x007A; alpha++) { - str = String.fromCharCode(alpha % 32); - arr = (new RegExp("\\c" + String.fromCharCode(alpha))).exec(str); - if ((arr === null) || (arr[0] !== str)) { - result = false; - } -} - -if (result !== true) { - $ERROR('#1: CharacterEscape :: c a - z'); -} diff --git a/tests/test_cases/built-ins/RegExp/S15.10.2.9_A1_T4.js b/tests/test_cases/built-ins/RegExp/S15.10.2.9_A1_T4.js deleted file mode 100755 index 37cfcb6d..00000000 --- a/tests/test_cases/built-ins/RegExp/S15.10.2.9_A1_T4.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - An escape sequence of the form \ followed by a nonzero decimal number n - matches the result of the nth set of capturing parentheses (see - 15.10.2.11) -es5id: 15.10.2.9_A1_T4 -description: > - Execute /\b(\w+) \2\b/.test("do you listen the the band") and - check results - - onlyStrict ----*/ - -__executed = /\b(\w+) \2\b/.test("do you listen the the band"); - -//CHECK#1 -if (__executed) { - $ERROR('#1: /\\b(\\w+) \\2\\b/.test("do you listen the the band") === false'); -} From 13062756f49532caecc7bdf03c970e19ff9ada35 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Mon, 3 Sep 2018 20:03:41 +0200 Subject: [PATCH 28/77] Fixed PyObjectWrapper, assignment op operator --- js2py/base.py | 7 ++++++- simple_test.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/js2py/base.py b/js2py/base.py index 5b46e307..23d04ccb 100644 --- a/js2py/base.py +++ b/js2py/base.py @@ -1191,10 +1191,15 @@ def get(self, prop): except: return undefined - def put(self, prop, val, throw=False): + def put(self, prop, val, op=None, throw=False): if not isinstance(prop, basestring): prop = prop.to_string().value try: + if isinstance(op, bool): + raise ValueError("Op must be a string") + elif op is not None: + if op: # increment operation + val = getattr(self.get(prop), OP_METHODS[op])(val) setattr(self.obj, prop, to_python(val)) except AttributeError: raise MakeError('TypeError', 'Read only object probably...') diff --git a/simple_test.py b/simple_test.py index 904dfa3c..3c3078c9 100644 --- a/simple_test.py +++ b/simple_test.py @@ -2,6 +2,8 @@ import time print("Testing ECMA 5...") + + assert js2py.eval_js('(new Date("2008-9-03T20:56:35.450686Z")).toString()') assert js2py.eval_js('/ser/.test("Mleko + ser to nabial")') @@ -27,8 +29,37 @@ assert str(err).startswith('SyntaxError: ') -print("Passed ECMA 5 simple tests!\n"+30*'-') +# Simple PyWrapper test +class Foo: + def __init__(self, bar): + self.bar = bar + self.bar_history = [] + + def set_bar(self, x): + import copy + self.bar_history.append(copy.deepcopy(self)) + self.bar = x + + def get_bar(self): + return self.bar + +pyobj = Foo(5) +context = js2py.EvalJs({'foo': pyobj}) +assert context.foo.bar == 5 +context.execute('foo.bar = 7') +assert context.foo.bar == 7 +context.execute('foo.bar += 15.22') +assert context.foo.bar == 22.22 +context.execute('foo.bar /= 2') +assert context.foo.bar == 11.11 +assert pyobj.bar == 11.11 +context.execute('foo.set_bar(foo.get_bar())') +assert context.foo.get_bar() == 11.11 +context.execute('foo.set_bar(33)') +assert context.foo.get_bar() == 33 +assert context.eval('foo.bar_history.push(foo.bar_history[1].get_bar());foo.bar_history[foo.bar_history.length-1].get_bar()') == 11.11 +print("Passed ECMA 5 simple tests!\n"+30*'-') print('Now harder tests - test on huge JS libraries:') From 0992e5174e6b03574eb1c0deaad026a42b0660a8 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sun, 4 Nov 2018 13:48:18 +0100 Subject: [PATCH 29/77] yapf --- js2py/__init__.py | 10 +- js2py/base.py | 1715 ++++-- js2py/constructors/jsarray.py | 40 +- js2py/constructors/jsarraybuffer.py | 27 +- js2py/constructors/jsboolean.py | 21 +- js2py/constructors/jsdate.py | 190 +- js2py/constructors/jsfloat32array.py | 64 +- js2py/constructors/jsfloat64array.py | 64 +- js2py/constructors/jsfunction.py | 21 +- js2py/constructors/jsint16array.py | 65 +- js2py/constructors/jsint32array.py | 64 +- js2py/constructors/jsint8array.py | 57 +- js2py/constructors/jsmath.py | 68 +- js2py/constructors/jsnumber.py | 33 +- js2py/constructors/jsobject.py | 94 +- js2py/constructors/jsregexp.py | 21 +- js2py/constructors/jsstring.py | 36 +- js2py/constructors/jsuint16array.py | 65 +- js2py/constructors/jsuint32array.py | 71 +- js2py/constructors/jsuint8array.py | 56 +- js2py/constructors/jsuint8clampedarray.py | 56 +- js2py/constructors/time_helpers.py | 121 +- js2py/es6/__init__.py | 19 +- js2py/evaljs.py | 50 +- js2py/host/console.py | 5 +- js2py/host/jseval.py | 17 +- js2py/host/jsfunctions.py | 106 +- js2py/internals/base.py | 414 +- js2py/internals/byte_trans.py | 182 +- js2py/internals/code.py | 30 +- js2py/internals/constructors/jsarray.py | 13 +- js2py/internals/constructors/jsboolean.py | 1 - js2py/internals/constructors/jsdate.py | 190 +- js2py/internals/constructors/jsfunction.py | 23 +- js2py/internals/constructors/jsmath.py | 54 +- js2py/internals/constructors/jsnumber.py | 17 +- js2py/internals/constructors/jsobject.py | 83 +- js2py/internals/constructors/jsregexp.py | 20 +- js2py/internals/constructors/jsstring.py | 5 +- js2py/internals/constructors/time_helpers.py | 121 +- js2py/internals/conversions.py | 30 +- js2py/internals/desc.py | 80 +- js2py/internals/esprima.js | 5664 ------------------ js2py/internals/fill_space.py | 105 +- js2py/internals/func_utils.py | 22 +- js2py/internals/opcodes.py | 191 +- js2py/internals/operations.py | 48 +- js2py/internals/prototypes/jsarray.py | 156 +- js2py/internals/prototypes/jsboolean.py | 15 +- js2py/internals/prototypes/jserror.py | 6 +- js2py/internals/prototypes/jsfunction.py | 35 +- js2py/internals/prototypes/jsjson.py | 89 +- js2py/internals/prototypes/jsnumber.py | 134 +- js2py/internals/prototypes/jsobject.py | 12 +- js2py/internals/prototypes/jsregexp.py | 17 +- js2py/internals/prototypes/jsstring.py | 125 +- js2py/internals/prototypes/jsutils.py | 98 +- js2py/internals/seval.py | 3 +- js2py/internals/simplex.py | 32 +- js2py/internals/space.py | 16 +- js2py/internals/speed.py | 18 +- js2py/internals/trans_utils.py | 3 +- js2py/legecy_translators/constants.py | 188 +- js2py/legecy_translators/exps.py | 42 +- js2py/legecy_translators/flow.py | 228 +- js2py/legecy_translators/functions.py | 66 +- js2py/legecy_translators/jsparser.py | 159 +- js2py/legecy_translators/nodevisitor.py | 394 +- js2py/legecy_translators/nparser.py | 1768 +++--- js2py/legecy_translators/objects.py | 115 +- js2py/legecy_translators/translator.py | 52 +- js2py/legecy_translators/utils.py | 47 +- js2py/node_import.py | 49 +- js2py/prototypes/jsarray.py | 174 +- js2py/prototypes/jsarraybuffer.py | 2 + js2py/prototypes/jsboolean.py | 10 +- js2py/prototypes/jserror.py | 8 +- js2py/prototypes/jsfunction.py | 23 +- js2py/prototypes/jsjson.py | 111 +- js2py/prototypes/jsnumber.py | 128 +- js2py/prototypes/jsobject.py | 10 +- js2py/prototypes/jsregexp.py | 19 +- js2py/prototypes/jsstring.py | 113 +- js2py/prototypes/jstypedarray.py | 120 +- js2py/py_node_modules/__init__.py | 2 +- js2py/pyjs.py | 71 +- js2py/translators/__init__.py | 8 +- js2py/translators/friendly_nodes.py | 264 +- js2py/translators/jsregexps.py | 80 +- js2py/translators/translating_nodes.py | 257 +- js2py/translators/translator.py | 59 +- js2py/utils/injector.py | 128 +- setup.cfg | 5 +- simple_test.py | 4 +- 94 files changed, 6196 insertions(+), 9716 deletions(-) delete mode 100644 js2py/internals/esprima.js diff --git a/js2py/__init__.py b/js2py/__init__.py index b6c7bab4..a7fe5979 100644 --- a/js2py/__init__.py +++ b/js2py/__init__.py @@ -17,7 +17,6 @@ # 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. @@ -63,11 +62,14 @@ """ __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'] +__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 - diff --git a/js2py/base.py b/js2py/base.py index 23d04ccb..6043ed0e 100644 --- a/js2py/base.py +++ b/js2py/base.py @@ -12,7 +12,6 @@ except: NUMPY_AVAILABLE = False - # python 3 support import six if six.PY3: @@ -28,13 +27,12 @@ def str_repr(s): else: return repr(s) + def MakeError(name, message): """Returns PyJsException with PyJsError inside""" return JsToPyException(ERRORS[name](Js(message))) - - def to_python(val): if not isinstance(val, PyJs): return val @@ -44,8 +42,8 @@ def to_python(val): # this can be either float or long/int better to assume its int/long when a whole number... v = val.value try: - i = int(v) if v==v else v # nan... - return v if i!=v else i + i = int(v) if v == v else v # nan... + return v if i != v else i except: return v elif isinstance(val, (PyJsString, PyJsBoolean)): @@ -54,7 +52,9 @@ def to_python(val): return val.__dict__['obj'] return JsObjectWrapper(val) -def to_dict(js_obj, known=None): # fixed recursion error in self referencing objects + +def to_dict(js_obj, + known=None): # fixed recursion error in self referencing objects res = {} if known is None: known = {} @@ -66,10 +66,14 @@ def to_dict(js_obj, known=None): # fixed recursion error in self referencing obj input = js_obj.get(name) output = to_python(input) if isinstance(output, JsObjectWrapper): - if output._obj.Class=='Object': + if output._obj.Class == 'Object': output = to_dict(output._obj, known) known[input] = output - elif output._obj.Class in ['Array','Int8Array','Uint8Array','Uint8ClampedArray','Int16Array','Uint16Array','Int32Array','Uint32Array','Float32Array','Float64Array']: + elif output._obj.Class in [ + 'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', + 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', + 'Float32Array', 'Float64Array' + ]: output = to_list(output._obj) known[input] = output res[name] = output @@ -77,7 +81,7 @@ def to_dict(js_obj, known=None): # fixed recursion error in self referencing obj def to_list(js_obj, known=None): - res = len(js_obj)*[None] + res = len(js_obj) * [None] if known is None: known = {} if js_obj in known: @@ -91,7 +95,11 @@ def to_list(js_obj, known=None): input = js_obj.get(str(name)) output = to_python(input) if isinstance(output, JsObjectWrapper): - if output._obj.Class in ['Array', 'Int8Array','Uint8Array','Uint8ClampedArray','Int16Array','Uint16Array','Int32Array','Uint32Array','Float32Array','Float64Array', 'Arguments']: + if output._obj.Class in [ + 'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', + 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', + 'Float32Array', 'Float64Array', 'Arguments' + ]: output = to_list(output._obj, known) known[input] = output elif output._obj.Class in ['Object']: @@ -102,7 +110,8 @@ def to_list(js_obj, known=None): def HJs(val): - if hasattr(val, '__call__'): # + if hasattr(val, '__call__'): # + @Js def PyWrapper(this, arguments, var=None): args = tuple(to_python(e) for e in arguments.to_list()) @@ -137,10 +146,11 @@ def Js(val, Clamped=False): return PyJsString(val, StringPrototype) elif isinstance(val, bool): return true if val else false - elif isinstance(val, float) or isinstance(val, int) or isinstance(val, long) or (NUMPY_AVAILABLE and isinstance(val, (numpy.int8,numpy.uint8, - numpy.int16,numpy.uint16, - numpy.int32,numpy.uint32, - numpy.float32,numpy.float64))): + elif isinstance(val, float) or isinstance(val, int) or isinstance( + val, long) or (NUMPY_AVAILABLE and isinstance( + val, + (numpy.int8, numpy.uint8, numpy.int16, numpy.uint16, + numpy.int32, numpy.uint32, numpy.float32, numpy.float64))): # This is supposed to speed things up. may not be the case if val in NUM_BANK: return NUM_BANK[val] @@ -162,12 +172,12 @@ def Js(val, Clamped=False): # return Js(mod) #elif isintance(val, ClassType): - elif isinstance(val, dict): # convert to object - temp = PyJsObject({}, ObjectPrototype) - for k, v in six.iteritems(val): - temp.put(Js(k), Js(v)) - return temp - elif isinstance(val, (list, tuple)): #Convert to array + elif isinstance(val, dict): # convert to object + temp = PyJsObject({}, ObjectPrototype) + for k, v in six.iteritems(val): + temp.put(Js(k), Js(v)) + return temp + elif isinstance(val, (list, tuple)): #Convert to array return PyJsArray(val, ArrayPrototype) # convert to typedarray elif isinstance(val, JsObjectWrapper): @@ -193,7 +203,7 @@ def Js(val, Clamped=False): return PyJsFloat32Array(val, Float32ArrayPrototype) elif val.dtype == numpy.float64: return PyJsFloat64Array(val, Float64ArrayPrototype) - else: # try to convert to js object + else: # try to convert to js object return py_wrap(val) #raise RuntimeError('Cant convert python type to js (%s)' % repr(val)) #try: @@ -211,27 +221,33 @@ def Js(val, Clamped=False): #except: # raise RuntimeError('Cant convert python type to js (%s)' % repr(val)) + def Type(val): try: return val.TYPE except: - raise RuntimeError('Invalid type: '+str(val)) + raise RuntimeError('Invalid type: ' + str(val)) + def is_data_descriptor(desc): return desc and ('value' in desc or 'writable' in desc) + def is_accessor_descriptor(desc): return desc and ('get' in desc or 'set' in desc) -def is_generic_descriptor(desc): - return desc and not (is_data_descriptor(desc) or is_accessor_descriptor(desc)) +def is_generic_descriptor(desc): + return desc and not (is_data_descriptor(desc) + or is_accessor_descriptor(desc)) ############################################################################## + class PyJs(object): - PRIMITIVES = frozenset(['String', 'Number', 'Boolean', 'Undefined', 'Null']) + PRIMITIVES = frozenset( + ['String', 'Number', 'Boolean', 'Undefined', 'Null']) TYPE = 'Object' Class = None extensible = True @@ -241,7 +257,7 @@ class PyJs(object): IS_CHILD_SCOPE = False value = None buff = None - + def __init__(self, value=None, prototype=None, extensible=False): '''Constructor for Number String and Boolean''' # I dont think this is needed anymore @@ -259,12 +275,12 @@ def __init__(self, value=None, prototype=None, extensible=False): self.prototype = prototype self.own = {} self.buff = None - + def is_undefined(self): - return self.Class=='Undefined' + return self.Class == 'Undefined' def is_null(self): - return self.Class=='Null' + return self.Class == 'Null' def is_primitive(self): return self.TYPE in self.PRIMITIVES @@ -290,32 +306,34 @@ def get_property(self, prop): def update_array(self): for i in range(self.get('length').to_uint32()): - self.put(str(i),Js(self.buff[i])) - - def get(self, prop): #external use! - #prop = prop.value - if self.Class=='Undefined' or self.Class=='Null': - raise MakeError('TypeError', 'Undefined and null dont have properties!') - if not isinstance(prop, basestring): - prop = prop.to_string().value - if not isinstance(prop, basestring): raise RuntimeError('Bug') - if NUMPY_AVAILABLE and prop.isdigit(): - if isinstance(self.buff,numpy.ndarray): - self.update_array() - cand = self.get_property(prop) - if cand is None: - return Js(None) - if is_data_descriptor(cand): - return cand['value'] - if cand['get'].is_undefined(): - return cand['get'] - return cand['get'].call(self) + self.put(str(i), Js(self.buff[i])) + + def get(self, prop): #external use! + #prop = prop.value + if self.Class == 'Undefined' or self.Class == 'Null': + raise MakeError('TypeError', + 'Undefined and null dont have properties!') + if not isinstance(prop, basestring): + prop = prop.to_string().value + if not isinstance(prop, basestring): raise RuntimeError('Bug') + if NUMPY_AVAILABLE and prop.isdigit(): + if isinstance(self.buff, numpy.ndarray): + self.update_array() + cand = self.get_property(prop) + if cand is None: + return Js(None) + if is_data_descriptor(cand): + return cand['value'] + if cand['get'].is_undefined(): + return cand['get'] + return cand['get'].call(self) def can_put(self, prop): #to check desc = self.get_own_property(prop) - if desc: #if we have this property + if desc: #if we have this property if is_accessor_descriptor(desc): - return desc['set'].is_callable() # Check if setter method is defined + return desc['set'].is_callable( + ) # Check if setter method is defined else: #data desc return desc['writable'] if self.prototype is not None: @@ -329,16 +347,16 @@ def can_put(self, prop): #to check return inherited['writable'] return False - def put(self, prop, val, op=None): #external use! '''Just like in js: self.prop op= val for example when op is '+' it will be self.prop+=val op can be either None for simple assignment or one of: * / % + - << >> & ^ |''' - if self.Class=='Undefined' or self.Class=='Null': - raise MakeError('TypeError', 'Undefined and null dont have properties!') + if self.Class == 'Undefined' or self.Class == 'Null': + raise MakeError('TypeError', + 'Undefined and null dont have properties!') if not isinstance(prop, basestring): - prop = prop.to_string().value + prop = prop.to_string().value if NUMPY_AVAILABLE and prop.isdigit(): if self.Class == 'Int8Array': val = Js(numpy.int8(val.to_number().value)) @@ -363,7 +381,7 @@ def put(self, prop, val, op=None): #external use! val = Js(numpy.float32(val.to_number().value)) elif self.Class == 'Float64Array': val = Js(numpy.float64(val.to_number().value)) - if isinstance(self.buff,numpy.ndarray): + if isinstance(self.buff, numpy.ndarray): self.buff[int(prop)] = int(val.to_number().value) #we need to set the value to the incremented one if op is not None: @@ -372,20 +390,30 @@ def put(self, prop, val, op=None): #external use! return val own_desc = self.get_own_property(prop) if is_data_descriptor(own_desc): - if self.Class in ['Array','Int8Array','Uint8Array','Uint8ClampedArray','Int16Array','Uint16Array','Int32Array','Uint32Array','Float32Array','Float64Array']: - self.define_own_property(prop, {'value':val}) + if self.Class in [ + 'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', + 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', + 'Float32Array', 'Float64Array' + ]: + self.define_own_property(prop, {'value': val}) else: self.own[prop]['value'] = val return val desc = self.get_property(prop) if is_accessor_descriptor(desc): - desc['set'].call(self, (val,)) + desc['set'].call(self, (val, )) else: - new = {'value' : val, - 'writable' : True, - 'configurable' : True, - 'enumerable' : True} - if self.Class in ['Array','Int8Array','Uint8Array','Uint8ClampedArray','Int16Array','Uint16Array','Int32Array','Uint32Array','Float32Array','Float64Array']: + new = { + 'value': val, + 'writable': True, + 'configurable': True, + 'enumerable': True + } + if self.Class in [ + 'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', + 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', + 'Float32Array', 'Float64Array' + ]: self.define_own_property(prop, new) else: self.own[prop] = new @@ -405,9 +433,11 @@ def delete(self, prop): return Js(True) return Js(False) - def default_value(self, hint=None): # made a mistake at the very early stage and made it to prefer string... caused lots! of problems + def default_value( + self, hint=None + ): # made a mistake at the very early stage and made it to prefer string... caused lots! of problems order = ('valueOf', 'toString') - if hint=='String' or (hint is None and self.Class=='Date'): + if hint == 'String' or (hint is None and self.Class == 'Date'): order = ('toString', 'valueOf') for meth_name in order: method = self.get(meth_name) @@ -415,61 +445,68 @@ def default_value(self, hint=None): # made a mistake at the very early stage an cand = method.call(self) if cand.is_primitive(): return cand - raise MakeError('TypeError', 'Cannot convert object to primitive value') - + raise MakeError('TypeError', + 'Cannot convert object to primitive value') - def define_own_property(self, prop, desc): #Internal use only. External through Object + def define_own_property(self, prop, + desc): #Internal use only. External through Object # prop must be a Py string. Desc is either a descriptor or accessor. #Messy method - raw translation from Ecma spec to prevent any bugs. # todo check this current = self.get_own_property(prop) extensible = self.extensible - if not current: #We are creating a new property + if not current: #We are creating a new property if not extensible: return False if is_data_descriptor(desc) or is_generic_descriptor(desc): - DEFAULT_DATA_DESC = {'value': undefined, #undefined - 'writable': False, - 'enumerable': False, - 'configurable': False} + DEFAULT_DATA_DESC = { + 'value': undefined, #undefined + 'writable': False, + 'enumerable': False, + 'configurable': False + } DEFAULT_DATA_DESC.update(desc) self.own[prop] = DEFAULT_DATA_DESC else: - DEFAULT_ACCESSOR_DESC = {'get': undefined, #undefined - 'set': undefined, #undefined - 'enumerable': False, - 'configurable': False} + DEFAULT_ACCESSOR_DESC = { + 'get': undefined, #undefined + 'set': undefined, #undefined + 'enumerable': False, + 'configurable': False + } DEFAULT_ACCESSOR_DESC.update(desc) self.own[prop] = DEFAULT_ACCESSOR_DESC return True - if not desc or desc==current: #We dont need to change anything. + if not desc or desc == current: #We dont need to change anything. return True configurable = current['configurable'] if not configurable: #Prevent changing configurable or enumerable if desc.get('configurable'): return False - if 'enumerable' in desc and desc['enumerable']!=current['enumerable']: + if 'enumerable' in desc and desc['enumerable'] != current[ + 'enumerable']: return False if is_generic_descriptor(desc): pass - elif is_data_descriptor(current)!=is_data_descriptor(desc): + elif is_data_descriptor(current) != is_data_descriptor(desc): if not configurable: return False if is_data_descriptor(current): del current['value'] del current['writable'] - current['set'] = undefined #undefined - current['get'] = undefined #undefined + current['set'] = undefined #undefined + current['get'] = undefined #undefined else: del current['set'] del current['get'] - current['value'] = undefined #undefined + current['value'] = undefined #undefined current['writable'] = False elif is_data_descriptor(current) and is_data_descriptor(desc): if not configurable: if not current['writable'] and desc.get('writable'): return False - if not current['writable'] and 'value' in desc and current['value']!=desc['value']: + if not current['writable'] and 'value' in desc and current[ + 'value'] != desc['value']: return False elif is_accessor_descriptor(current) and is_accessor_descriptor(desc): if not configurable: @@ -482,104 +519,110 @@ def define_own_property(self, prop, desc): #Internal use only. External through #these methods will work only for Number class def is_infinity(self): - assert self.Class=='Number' - return self.value==float('inf') or self.value==-float('inf') + assert self.Class == 'Number' + return self.value == float('inf') or self.value == -float('inf') def is_nan(self): - assert self.Class=='Number' - return self.value!=self.value #nan!=nan evaluates to true + assert self.Class == 'Number' + return self.value != self.value #nan!=nan evaluates to true def is_finite(self): return not (self.is_nan() or self.is_infinity()) + #Type Conversions. to_type. All must return pyjs subclass instance def to_primitive(self, hint=None): if self.is_primitive(): return self - if hint is None and (self.Class=='Number' or self.Class=='Boolean'): # favour number for Class== Number or Boolean default = String + if hint is None and ( + self.Class == 'Number' or self.Class == 'Boolean' + ): # favour number for Class== Number or Boolean default = String hint = 'Number' return self.default_value(hint) def to_boolean(self): typ = Type(self) - if typ=='Boolean': #no need to convert + if typ == 'Boolean': #no need to convert return self - elif typ=='Null' or typ=='Undefined': #they are both always false + elif typ == 'Null' or typ == 'Undefined': #they are both always false return false - elif typ=='Number' or typ=='String': #false only for 0, '' and NaN - return Js(bool(self.value and self.value==self.value)) # test for nan (nan -> flase) - else: #object - always true + elif typ == 'Number' or typ == 'String': #false only for 0, '' and NaN + return Js(bool( + self.value + and self.value == self.value)) # test for nan (nan -> flase) + else: #object - always true return true def to_number(self): typ = Type(self) - if typ=='Null': #null is 0 + if typ == 'Null': #null is 0 return Js(0) - elif typ=='Undefined': # undefined is NaN + elif typ == 'Undefined': # undefined is NaN return NaN - elif typ=='Boolean': # 1 for true 0 for false + elif typ == 'Boolean': # 1 for true 0 for false return Js(int(self.value)) - elif typ=='Number':# or self.Class=='Number': # no need to convert + elif typ == 'Number': # or self.Class=='Number': # no need to convert return self - elif typ=='String': - s = self.value.strip() #Strip white space - if not s: # '' is simply 0 + elif typ == 'String': + s = self.value.strip() #Strip white space + if not s: # '' is simply 0 return Js(0) - if 'x' in s or 'X' in s[:3]: #hex (positive only) - try: # try to convert + if 'x' in s or 'X' in s[:3]: #hex (positive only) + try: # try to convert num = int(s, 16) - except ValueError: # could not convert > NaN + except ValueError: # could not convert > NaN return NaN return Js(num) - sign = 1 #get sign + sign = 1 #get sign if s[0] in '+-': - if s[0]=='-': + if s[0] == '-': sign = -1 s = s[1:] - if s=='Infinity': #Check for infinity keyword. 'NaN' will be NaN anyway. - return Js(sign*float('inf')) - try: #decimal try - num = sign*float(s) # Converted + if s == 'Infinity': #Check for infinity keyword. 'NaN' will be NaN anyway. + return Js(sign * float('inf')) + try: #decimal try + num = sign * float(s) # Converted except ValueError: - return NaN # could not convert to decimal > return NaN + return NaN # could not convert to decimal > return NaN return Js(num) - else: #object - most likely it will be NaN. + else: #object - most likely it will be NaN. return self.to_primitive('Number').to_number() def to_string(self): typ = Type(self) - if typ=='Null': + if typ == 'Null': return Js('null') - elif typ=='Undefined': + elif typ == 'Undefined': return Js('undefined') - elif typ=='Boolean': + elif typ == 'Boolean': return Js('true') if self.value else Js('false') - elif typ=='Number': #or self.Class=='Number': + elif typ == 'Number': #or self.Class=='Number': if self.is_nan(): return Js('NaN') elif self.is_infinity(): - sign = '-' if self.value<0 else '' - return Js(sign+'Infinity') - elif isinstance(self.value, long) or self.value.is_integer(): # dont print .0 + sign = '-' if self.value < 0 else '' + return Js(sign + 'Infinity') + elif isinstance(self.value, + long) or self.value.is_integer(): # dont print .0 return Js(unicode(int(self.value))) - return Js(unicode(self.value)) # accurate enough - elif typ=='String': + return Js(unicode(self.value)) # accurate enough + elif typ == 'String': return self - else: #object + else: #object return self.to_primitive('String').to_string() - def to_object(self): typ = self.TYPE - if typ=='Null' or typ=='Undefined': - raise MakeError('TypeError', 'undefined or null can\'t be converted to object') - elif typ=='Boolean': # Unsure here... todo repair here + if typ == 'Null' or typ == 'Undefined': + raise MakeError('TypeError', + 'undefined or null can\'t be converted to object') + elif typ == 'Boolean': # Unsure here... todo repair here return Boolean.create(self) - elif typ=='Number': #? + elif typ == 'Number': #? return Number.create(self) - elif typ=='String': #? + elif typ == 'String': #? return String.create(self) - else: #object + else: #object return self def to_int32(self): @@ -595,14 +638,15 @@ def strict_equality_comparison(self, other): def cok(self): """Check object coercible""" if self.Class in ('Undefined', 'Null'): - raise MakeError('TypeError', 'undefined or null can\'t be converted to object') + raise MakeError('TypeError', + 'undefined or null can\'t be converted to object') def to_int(self): num = self.to_number() if num.is_nan(): return 0 elif num.is_infinity(): - return 10**20 if num.value>0 else -10**20 + return 10**20 if num.value > 0 else -10**20 return int(num.value) def to_uint32(self): @@ -624,44 +668,48 @@ def to_int16(self): int16 = int(num.value) % 2**16 return int(int16 - 2**16 if int16 >= 2**15 else int16) - - def same_as(self, other): typ = Type(self) - if typ!=other.Class: + if typ != other.Class: return False - if typ=='Undefined' or typ=='Null': + if typ == 'Undefined' or typ == 'Null': return True - if typ=='Boolean' or typ=='Number' or typ=='String': - return self.value==other.value - else: #object - return self is other #Id compare. + if typ == 'Boolean' or typ == 'Number' or typ == 'String': + return self.value == other.value + else: #object + return self is other #Id compare. #Not to be used by translation (only internal use) def __getitem__(self, item): - return self.get(str(item) if not isinstance(item, PyJs) else item.to_string().value) + return self.get( + str(item) if not isinstance(item, PyJs) else item.to_string(). + value) def __setitem__(self, item, value): - self.put(str(item) if not isinstance(item, PyJs) else item.to_string().value, Js(value)) + self.put( + str(item) if not isinstance(item, PyJs) else + item.to_string().value, Js(value)) def __len__(self): try: return self.get('length').to_uint32() except: - raise TypeError('This object (%s) does not have length property'%self.Class) + raise TypeError( + 'This object (%s) does not have length property' % self.Class) + #Oprators------------- #Unary, other will be implemented as functions. Increments and decrements # will be methods of Number class - def __neg__(self): #-u + def __neg__(self): #-u return Js(-self.to_number().value) - def __pos__(self): #+u + def __pos__(self): #+u return self.to_number() - def __invert__(self): #~u + def __invert__(self): #~u return Js(Js(~self.to_int32()).to_int32()) - def neg(self): # !u cant do 'not u' :( + def neg(self): # !u cant do 'not u' :( return Js(not self.to_boolean().value) def __nonzero__(self): @@ -674,7 +722,7 @@ def typeof(self): if self.is_callable(): return Js('function') typ = Type(self).lower() - if typ=='null': + if typ == 'null': typ = 'object' return Js(typ) @@ -695,7 +743,6 @@ def __rshift__(self, other): shiftCount = rnum & 0x1F return Js(Js(lnum >> shiftCount).to_int32()) - # >>> def pyjs_bshift(self, other): lnum = self.to_uint32() @@ -728,47 +775,47 @@ def __or__(self, other): def __add__(self, other): a = self.to_primitive() b = other.to_primitive() - if a.TYPE=='String' or b.TYPE=='String': - return Js(a.to_string().value+b.to_string().value) + if a.TYPE == 'String' or b.TYPE == 'String': + return Js(a.to_string().value + b.to_string().value) a = a.to_number() b = b.to_number() - return Js(a.value+b.value) + return Js(a.value + b.value) # - def __sub__(self, other): - return Js(self.to_number().value-other.to_number().value) + return Js(self.to_number().value - other.to_number().value) #Multiplicative operators # *, / and % are implemented here # * def __mul__(self, other): - return Js(self.to_number().value*other.to_number().value) + return Js(self.to_number().value * other.to_number().value) # / def __div__(self, other): a = self.to_number().value b = other.to_number().value if b: - return Js(a/b) - if not a or a!=a: + return Js(a / b) + if not a or a != a: return NaN - return Infinity if a>0 else -Infinity + return Infinity if a > 0 else -Infinity # % def __mod__(self, other): a = self.to_number().value b = other.to_number().value - if abs(a)==float('inf') or not b: + if abs(a) == float('inf') or not b: return NaN - if abs(b)==float('inf'): + if abs(b) == float('inf'): return Js(a) - pyres = Js(a%b) #different signs in python and javascript - #python has the same sign as b and js has the same - #sign as a. - if a<0 and pyres.value>0: + pyres = Js(a % b) #different signs in python and javascript + #python has the same sign as b and js has the same + #sign as a. + if a < 0 and pyres.value > 0: pyres.value -= abs(b) - elif a>0 and pyres.value<0: + elif a > 0 and pyres.value < 0: pyres.value += abs(b) return Js(pyres) @@ -784,17 +831,17 @@ def abstract_relational_comparison(self, other, self_first=True): result is PyJs type: bool or undefined''' px = self.to_primitive('Number') py = other.to_primitive('Number') - if not self_first: #reverse order + if not self_first: #reverse order px, py = py, px - if not (px.Class=='String' and py.Class=='String'): + if not (px.Class == 'String' and py.Class == 'String'): px, py = px.to_number(), py.to_number() if px.is_nan() or py.is_nan(): return undefined - return Js(px.value>>b""" return a.pyjs_bshift(b) @@ -986,24 +1045,29 @@ def PyJsComma(a, b): pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError('SyntaxError', msg) -class PyJsSwitchException(Exception): pass +class PyJsSwitchException(Exception): + pass PyJs.MakeError = staticmethod(MakeError) + def JsToPyException(js): temp = PyJsException() temp.mes = js return temp + def PyExceptionToJs(py): return py.mes + #Scope class it will hold all the variables accessible to user class Scope(PyJs): Class = 'global' extensible = True IS_CHILD_SCOPE = True + # todo speed up # in order to speed up this very important class the top scope should behave differently than # child scopes, child scope should not have this property descriptor thing because they cant be changed anyway @@ -1017,11 +1081,16 @@ def __init__(self, scope, closure=None): self.own = {} for k, v in six.iteritems(scope): # set all the global items - self.define_own_property(k, {'value': v, 'configurable': False, - 'writable': False, 'enumerable': False}) + self.define_own_property( + k, { + 'value': v, + 'configurable': False, + 'writable': False, + 'enumerable': False + }) else: # not global, less powerful but faster closure. - self.own = scope # simple dictionary which maps name directly to js object. + self.own = scope # simple dictionary which maps name directly to js object. def register(self, lval): # registered keeps only global registered variables @@ -1030,11 +1099,16 @@ def register(self, lval): if lval in self.own: self.own[lval]['configurable'] = False else: - self.define_own_property(lval, {'value': undefined, 'configurable': False, - 'writable': True, 'enumerable': True}) + self.define_own_property( + lval, { + 'value': undefined, + 'configurable': False, + 'writable': True, + 'enumerable': True + }) elif lval not in self.own: # define in local scope since it has not been defined yet - self.own[lval] = undefined # default value + self.own[lval] = undefined # default value def registers(self, lvals): """register multiple variables""" @@ -1049,8 +1123,8 @@ def put(self, lval, val, op=None): # trying to put in local scope # we dont know yet in which scope we should place this var if lval in self.own: - if op: # increment operation - val = getattr(self.own[lval], OP_METHODS[op])(val) + if op: # increment operation + val = getattr(self.own[lval], OP_METHODS[op])(val) self.own[lval] = val return val else: @@ -1058,8 +1132,13 @@ def put(self, lval, val, op=None): return self.prototype.put(lval, val, op) def force_own_put(self, prop, val, configurable=False): - if self.prototype is None: # global scope - self.own[prop] = {'value': val, 'writable': True, 'enumerable':True, 'configurable':configurable} + if self.prototype is None: # global scope + self.own[prop] = { + 'value': val, + 'writable': True, + 'enumerable': True, + 'configurable': configurable + } else: self.own[prop] = val @@ -1105,11 +1184,14 @@ def __repr__(self): def to_python(self): return to_python(self) + class This(Scope): IS_CHILD_SCOPE = False + def get(self, prop, throw=False): return Scope.get(self, prop, throw) + class JsObjectWrapper(object): def __init__(self, obj): self.__dict__['_obj'] = obj @@ -1117,7 +1199,7 @@ def __init__(self, obj): def __call__(self, *args): args = tuple(Js(e) for e in args) if '_prop_of' in self.__dict__: - parent, meth = self.__dict__['_prop_of'] + parent, meth = self.__dict__['_prop_of'] return to_python(parent._obj.callprop(meth, *args)) return to_python(self._obj(*args)) @@ -1127,6 +1209,7 @@ def __getattr__(self, item): def PyJsInstanceInit(*args): args = tuple(Js(e) for e in args) return self._obj.create(*args).to_python() + return PyJsInstanceInit cand = to_python(self._obj.get(str(item))) # handling method calling... obj.meth(). Value of this in meth should be self @@ -1147,17 +1230,25 @@ def __setitem__(self, item, value): self._obj.put(str(item), Js(value)) def __iter__(self): - if self._obj.Class in ['Array', 'Int8Array','Uint8Array','Uint8ClampedArray','Int16Array','Uint16Array','Int32Array','Uint32Array','Float32Array','Float64Array']: + if self._obj.Class in [ + 'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', + 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', + 'Float32Array', 'Float64Array' + ]: return iter(self.to_list()) - elif self._obj.Class=='Object': + elif self._obj.Class == 'Object': return iter(self.to_dict()) else: - raise MakeError('TypeError', '%s is not iterable in Python' % self._obj.Class) + raise MakeError('TypeError', + '%s is not iterable in Python' % self._obj.Class) def __repr__(self): if self._obj.is_primitive() or self._obj.is_callable(): return repr(self._obj) - elif self._obj.Class in ('Array', 'Int8Array','Uint8Array','Uint8ClampedArray','Int16Array','Uint16Array','Int32Array','Uint32Array','Float32Array','Float64Array', 'Arguments'): + elif self._obj.Class in ('Array', 'Int8Array', 'Uint8Array', + 'Uint8ClampedArray', 'Int16Array', + 'Uint16Array', 'Int32Array', 'Uint32Array', + 'Float32Array', 'Float64Array', 'Arguments'): return repr(self.to_list()) return repr(self.to_dict()) @@ -1176,8 +1267,10 @@ def to_dict(self): def to_list(self): return to_list(self.__dict__['_obj']) + class PyObjectWrapper(PyJs): Class = 'PyObjectWrapper' + def __init__(self, obj): self.obj = obj @@ -1198,8 +1291,8 @@ def put(self, prop, val, op=None, throw=False): if isinstance(op, bool): raise ValueError("Op must be a string") elif op is not None: - if op: # increment operation - val = getattr(self.get(prop), OP_METHODS[op])(val) + if op: # increment operation + val = getattr(self.get(prop), OP_METHODS[op])(val) setattr(self.obj, prop, to_python(val)) except AttributeError: raise MakeError('TypeError', 'Read only object probably...') @@ -1218,7 +1311,6 @@ def __call__(self, *args): raise MakeError('Error', message) return py_wrap(py_res) - def callprop(self, prop, *args): py_args = tuple(to_python(e) for e in args) if not isinstance(prop, basestring): @@ -1248,20 +1340,21 @@ def to_py(self): def py_wrap(py): - if isinstance(py, (FunctionType, BuiltinFunctionType, MethodType, BuiltinMethodType, - dict, int, str, bool, float, list, tuple, long, basestring)) or py is None : + if isinstance(py, (FunctionType, BuiltinFunctionType, MethodType, + BuiltinMethodType, dict, int, str, bool, float, list, + tuple, long, basestring)) or py is None: return HJs(py) return PyObjectWrapper(py) - - ############################################################################## #Define types + #Object class PyJsObject(PyJs): Class = 'Object' + def __init__(self, prop_descs={}, prototype=None, extensible=True): self.prototype = prototype self.extensible = extensible @@ -1273,13 +1366,13 @@ def __repr__(self): return repr(self.to_python().to_dict()) - ObjectPrototype = PyJsObject() #Function class PyJsFunction(PyJs): Class = 'Function' + def __init__(self, func, prototype=None, extensible=True, source=None): cand = fix_js_args(func) has_scope = cand is func @@ -1287,25 +1380,46 @@ def __init__(self, func, prototype=None, extensible=True, source=None): self.argcount = six.get_function_code(func).co_argcount - 2 - has_scope self.code = func self.source = source if source else '{ [python code] }' - self.func_name = func.__name__ if not func.__name__.startswith('PyJs_anonymous') else '' + self.func_name = func.__name__ if not func.__name__.startswith( + 'PyJs_anonymous') else '' self.extensible = extensible self.prototype = prototype self.own = {} #set own property length to the number of arguments - self.define_own_property('length', {'value': Js(self.argcount), 'writable': False, - 'enumerable': False, 'configurable': False}) + self.define_own_property( + 'length', { + 'value': Js(self.argcount), + 'writable': False, + 'enumerable': False, + 'configurable': False + }) if self.func_name: - self.define_own_property('name', {'value': Js(self.func_name), 'writable': False, - 'enumerable': False, 'configurable': True}) + self.define_own_property( + 'name', { + 'value': Js(self.func_name), + 'writable': False, + 'enumerable': False, + 'configurable': True + }) # set own prototype proto = Js({}) # constructor points to this function - proto.define_own_property('constructor',{'value': self, 'writable': True, - 'enumerable': False, 'configurable': True}) - self.define_own_property('prototype', {'value': proto, 'writable': True, - 'enumerable': False, 'configurable': False}) + proto.define_own_property( + 'constructor', { + 'value': self, + 'writable': True, + 'enumerable': False, + 'configurable': True + }) + self.define_own_property( + 'prototype', { + 'value': proto, + 'writable': True, + 'enumerable': False, + 'configurable': False + }) def _set_name(self, name): '''name is py type''' @@ -1315,7 +1429,7 @@ def _set_name(self, name): def construct(self, *args): proto = self.get('prototype') - if not proto.is_object(): # set to standard prototype + if not proto.is_object(): # set to standard prototype proto = ObjectPrototype obj = PyJsObject(prototype=proto) cand = self.call(obj, *args) @@ -1335,22 +1449,25 @@ def call(self, this, args=()): (but they will be present in arguments object). ''' if not hasattr(args, '__iter__'): #get rid of it later - args = (args,) - args = tuple(Js(e) for e in args) # this wont be needed later + args = (args, ) + args = tuple(Js(e) for e in args) # this wont be needed later - arguments = PyJsArguments(args, self) # tuple will be converted to arguments object. - arglen = self.argcount #function expects this number of args. - if len(args)>arglen: + arguments = PyJsArguments( + args, self) # tuple will be converted to arguments object. + arglen = self.argcount #function expects this number of args. + if len(args) > arglen: args = args[0:arglen] - elif len(args)>': '__rshift__', - '&': '__and__', - '^': '__xor__', - '|': '__or__', - '>>>': 'pyjs_bshift'} +OP_METHODS = { + '*': '__mul__', + '/': '__div__', + '%': '__mod__', + '+': '__add__', + '-': '__sub__', + '<<': '__lshift__', + '>>': '__rshift__', + '&': '__and__', + '^': '__xor__', + '|': '__or__', + '>>>': 'pyjs_bshift' +} + def Empty(): return Js(None) + #Number class PyJsNumber(PyJs): #Note i dont implement +0 and -0. Just 0. TYPE = 'Number' Class = 'Number' - NumberPrototype = PyJsObject({}, ObjectPrototype) NumberPrototype.Class = 'Number' NumberPrototype.value = 0 @@ -1448,6 +1590,8 @@ class PyJsNumber(PyJs): #Note i dont implement +0 and -0. Just 0. CHAR_BANK = {} NUM_BANK = {} PyJs.CHAR_BANK = CHAR_BANK + + #String # Different than implementation design in order to improve performance #for example I dont create separate property for each character in string, it would take ages. @@ -1455,31 +1599,36 @@ class PyJsString(PyJs): TYPE = 'String' Class = 'String' extensible = False + def __init__(self, value=None, prototype=None): '''Constructor for Number String and Boolean''' if not isinstance(value, basestring): - raise TypeError # this will be internal error + raise TypeError # this will be internal error self.value = value self.prototype = prototype self.own = {} - # this should be optimized because its mych slower than python str creation (about 50 times!) + # this should be optimized because its mych slower than python str creation (about 50 times!) # Dont create separate properties for every index. Just - self.own['length'] = {'value': Js(len(value)), 'writable': False, - 'enumerable': False, 'configurable': False} - if len(value)==1: - CHAR_BANK[value] = self #, 'writable': False, - # 'enumerable': True, 'configurable': False} + self.own['length'] = { + 'value': Js(len(value)), + 'writable': False, + 'enumerable': False, + 'configurable': False + } + if len(value) == 1: + CHAR_BANK[value] = self #, 'writable': False, + # 'enumerable': True, 'configurable': False} def get(self, prop): if not isinstance(prop, basestring): - prop = prop.to_string().value + prop = prop.to_string().value try: index = int(prop) - if index<0: + if index < 0: return undefined char = self.value[index] if char not in CHAR_BANK: - Js(char) # this will add char to CHAR BANK + Js(char) # this will add char to CHAR BANK return CHAR_BANK[char] except Exception: pass @@ -1499,11 +1648,13 @@ def __iter__(self): CHAR_BANK[''] = Js('') + #Boolean class PyJsBoolean(PyJs): TYPE = 'Boolean' Class = 'Boolean' + BooleanPrototype = PyJsObject({}, ObjectPrototype) BooleanPrototype.Class = 'Boolean' BooleanPrototype.value = False @@ -1516,69 +1667,94 @@ class PyJsBoolean(PyJs): class PyJsUndefined(PyJs): TYPE = 'Undefined' Class = 'Undefined' + def __init__(self): pass + undefined = PyJsUndefined() + #Null class PyJsNull(PyJs): TYPE = 'Null' Class = 'Null' + def __init__(self): pass + + null = PyJsNull() PyJs.null = null + class PyJsArray(PyJs): Class = 'Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsArrayBuffer(PyJs): Class = 'ArrayBuffer' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsInt8Array(PyJs): Class = 'Int8Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsUint8Array(PyJs): Class = 'Uint8Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsUint8ClampedArray(PyJs): Class = 'Uint8ClampedArray' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsInt16Array(PyJs): Class = 'Int16Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsUint16Array(PyJs): Class = 'Uint16Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsInt32Array(PyJs): Class = 'Int32Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsUint32Array(PyJs): Class = 'Uint32Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsFloat32Array(PyJs): Class = 'Float32Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + class PyJsFloat64Array(PyJs): Class = 'Float64Array' + def __init__(self, arr=[], prototype=None): self.extensible = True self.prototype = prototype - self.own = {'length' : {'value': Js(0), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': Js(0), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } for i, e in enumerate(arr): - self.define_own_property(str(i), {'value': Js(e), 'writable': True, - 'enumerable': True, 'configurable': True}) + self.define_own_property( + str(i), { + 'value': Js(e), + 'writable': True, + 'enumerable': True, + 'configurable': True + }) + def define_own_property(self, prop, desc): old_len_desc = self.get_own_property('length') - old_len = old_len_desc['value'].value # value is js type so convert to py. - if prop=='length': + old_len = old_len_desc[ + 'value'].value # value is js type so convert to py. + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc) - new_len = desc['value'].to_uint32() - if new_len!=desc['value'].to_number().value: + new_len = desc['value'].to_uint32() + if new_len != desc['value'].to_number().value: raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = Js(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = Js(old_len+1) + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = Js(old_len + 1) if not new_writable: new_desc['writable'] = False PyJs.define_own_property(self, prop, new_desc) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = Js(index + 1) return True else: return PyJs.define_own_property(self, prop, desc) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] def __repr__(self): return repr(self.to_python().to_list()) + ArrayPrototype = PyJsArray([], ObjectPrototype) ArrayBufferPrototype = PyJsArrayBuffer([], ObjectPrototype) @@ -2375,32 +2764,46 @@ def __repr__(self): Float64ArrayPrototype = PyJsFloat64Array([], ObjectPrototype) + class PyJsArguments(PyJs): Class = 'Arguments' + def __init__(self, args, callee): self.own = {} self.extensible = True self.prototype = ObjectPrototype - self.define_own_property('length', {'value': Js(len(args)), 'writable': True, - 'enumerable': False, 'configurable': True}) - self.define_own_property('callee', {'value': callee, 'writable': True, - 'enumerable': False, 'configurable': True}) + self.define_own_property( + 'length', { + 'value': Js(len(args)), + 'writable': True, + 'enumerable': False, + 'configurable': True + }) + self.define_own_property( + 'callee', { + 'value': callee, + 'writable': True, + 'enumerable': False, + 'configurable': True + }) for i, e in enumerate(args): self.put(str(i), Js(e)) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] #We can define function proto after number proto because func uses number in its init FunctionPrototype = PyJsFunction(Empty, ObjectPrototype) FunctionPrototype.own['name']['value'] = Js('') - # I will not rewrite RegExp engine from scratch. I will use re because its much faster. # I have to only make sure that I am handling all the differences correctly. REGEXP_DB = {} + class PyJsRegExp(PyJs): Class = 'RegExp' extensible = True @@ -2416,10 +2819,10 @@ def __init__(self, regexp, prototype=None): # 'lastpos': -1, # 'matches': {}} flags = '' - if not regexp[-1]=='/': + if not regexp[-1] == '/': #contains some flags (allowed are i, g, m - spl = regexp.rfind('/') - flags = set(regexp[spl+1:]) + spl = regexp.rfind('/') + flags = set(regexp[spl + 1:]) self.value = regexp[1:spl] if 'g' in flags: self.glob = True @@ -2434,39 +2837,67 @@ def __init__(self, regexp, prototype=None): if self.value in REGEXP_DB: self.pat = REGEXP_DB[regexp] else: - comp = 'None' + comp = 'None' # we have to check whether pattern is valid. # also this will speed up matching later # todo critical fix patter conversion etc. ..!!!!! # ugly hacks porting js reg exp to py reg exp works in 99% of cases ;) - possible_fixes = [ - (u'[]', u'[\0]'), - (u'[^]', u'[^\0]'), - (u'nofix1791', u'nofix1791') - ] + possible_fixes = [(u'[]', u'[\0]'), (u'[^]', u'[^\0]'), + (u'nofix1791', u'nofix1791')] reg = self.value for fix, rep in possible_fixes: comp = REGEXP_CONVERTER._interpret_regexp(reg, flags) #print 'reg -> comp', reg, '->', comp try: - self.pat = re.compile(comp, self.ignore_case | self.multiline) + self.pat = re.compile( + comp, self.ignore_case | self.multiline) #print reg, '->', comp break except: reg = reg.replace(fix, rep) - # print 'Fix', fix, '->', rep, '=', reg + # print 'Fix', fix, '->', rep, '=', reg else: raise REGEXP_DB[regexp] = self.pat except: #print 'Invalid pattern but fuck it', self.value, comp - raise MakeError('SyntaxError', 'Invalid RegExp pattern: %s -> %s'% (repr(self.value), repr(comp))) + raise MakeError( + 'SyntaxError', + 'Invalid RegExp pattern: %s -> %s' % (repr(self.value), + repr(comp))) # now set own properties: - self.own = {'source' : {'value': Js(self.value), 'enumerable': False, 'writable': False, 'configurable': False}, - 'global' : {'value': Js(self.glob), 'enumerable': False, 'writable': False, 'configurable': False}, - 'ignoreCase' : {'value': Js(bool(self.ignore_case)), 'enumerable': False, 'writable': False, 'configurable': False}, - 'multiline' : {'value': Js(bool(self.multiline)), 'enumerable': False, 'writable': False, 'configurable': False}, - 'lastIndex' : {'value': Js(0), 'enumerable': False, 'writable': True, 'configurable': False}} + self.own = { + 'source': { + 'value': Js(self.value), + 'enumerable': False, + 'writable': False, + 'configurable': False + }, + 'global': { + 'value': Js(self.glob), + 'enumerable': False, + 'writable': False, + 'configurable': False + }, + 'ignoreCase': { + 'value': Js(bool(self.ignore_case)), + 'enumerable': False, + 'writable': False, + 'configurable': False + }, + 'multiline': { + 'value': Js(bool(self.multiline)), + 'enumerable': False, + 'writable': False, + 'configurable': False + }, + 'lastIndex': { + 'value': Js(0), + 'enumerable': False, + 'writable': True, + 'configurable': False + } + } def match(self, string, pos): '''string is of course py string''' @@ -2497,15 +2928,15 @@ def match(self, string, pos): # return self.match(string, pos) - def JsRegExp(source): # Takes regexp literal! return PyJsRegExp(source, RegExpPrototype) + RegExpPrototype = PyJsRegExp('/(?:)/', ObjectPrototype) ####Exceptions: -default_attrs = {'writable':True, 'enumerable':False, 'configurable':True} +default_attrs = {'writable': True, 'enumerable': False, 'configurable': True} def fill_in_props(obj, props, default_desc): @@ -2514,10 +2945,10 @@ def fill_in_props(obj, props, default_desc): obj.define_own_property(prop, default_desc) - class PyJsError(PyJs): Class = 'Error' extensible = True + def __init__(self, message=None, prototype=None): self.prototype = prototype self.own = {} @@ -2525,43 +2956,58 @@ def __init__(self, message=None, prototype=None): self.put('message', Js(message).to_string()) self.own['message']['enumerable'] = False + ErrorPrototype = PyJsError(Js(''), ObjectPrototype) + + @Js def Error(message): - return PyJsError(None if message.is_undefined() else message, ErrorPrototype) + return PyJsError(None if message.is_undefined() else message, + ErrorPrototype) + + Error.create = Error -err = {'name': 'Error', - 'constructor': Error} +err = {'name': 'Error', 'constructor': Error} fill_in_props(ErrorPrototype, err, default_attrs) -Error.define_own_property('prototype', {'value': ErrorPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Error.define_own_property( + 'prototype', { + 'value': ErrorPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) + def define_error_type(name): TypeErrorPrototype = PyJsError(None, ErrorPrototype) + @Js def TypeError(message): - return PyJsError(None if message.is_undefined() else message, TypeErrorPrototype) - err = {'name': name, - 'constructor': TypeError} + return PyJsError(None if message.is_undefined() else message, + TypeErrorPrototype) + + err = {'name': name, 'constructor': TypeError} fill_in_props(TypeErrorPrototype, err, default_attrs) - TypeError.define_own_property('prototype', {'value': TypeErrorPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) + TypeError.define_own_property( + 'prototype', { + 'value': TypeErrorPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) ERRORS[name] = TypeError + ERRORS = {'Error': Error} ERROR_NAMES = ['Eval', 'Type', 'Range', 'Reference', 'Syntax', 'URI'] for e in ERROR_NAMES: - define_error_type(e+'Error') - + define_error_type(e + 'Error') ############################################################################## # Import and fill prototypes here. + #this works only for data properties def fill_prototype(prototype, Class, attrs, constructor=False): for i in dir(Class): @@ -2569,13 +3015,13 @@ def fill_prototype(prototype, Class, attrs, constructor=False): if six.PY2: if hasattr(e, '__func__'): temp = PyJsFunction(e.__func__, FunctionPrototype) - attrs = dict((k,v) for k,v in attrs.iteritems()) + attrs = dict((k, v) for k, v in attrs.iteritems()) attrs['value'] = temp prototype.define_own_property(i, attrs) else: if hasattr(e, '__call__') and not i.startswith('__'): temp = PyJsFunction(e, FunctionPrototype) - attrs = dict((k,v) for k,v in attrs.items()) + attrs = dict((k, v) for k, v in attrs.items()) attrs['value'] = temp prototype.define_own_property(i, attrs) if constructor: @@ -2583,30 +3029,37 @@ def fill_prototype(prototype, Class, attrs, constructor=False): prototype.define_own_property('constructor', attrs) - - PyJs.undefined = undefined PyJs.Js = staticmethod(Js) from .prototypes import jsfunction, jsobject, jsnumber, jsstring, jsboolean, jsarray, jsregexp, jserror, jsarraybuffer, jstypedarray - #Object proto fill_prototype(ObjectPrototype, jsobject.ObjectPrototype, default_attrs) + + #Define __proto__ accessor (this cant be done by fill_prototype since) @Js def __proto__(): return this.prototype if this.prototype is not None else null + + getter = __proto__ + + @Js def __proto__(val): if val.is_object(): this.prototype = val -setter = __proto__ -ObjectPrototype.define_own_property('__proto__', {'set': setter, - 'get': getter, - 'enumerable': False, - 'configurable':True}) + + +setter = __proto__ +ObjectPrototype.define_own_property('__proto__', { + 'set': setter, + 'get': getter, + 'enumerable': False, + 'configurable': True +}) #Function proto fill_prototype(FunctionPrototype, jsfunction.FunctionPrototype, default_attrs) @@ -2619,25 +3072,35 @@ def __proto__(val): #Array proto fill_prototype(ArrayPrototype, jsarray.ArrayPrototype, default_attrs) # ArrayBuffer proto -fill_prototype(ArrayBufferPrototype, jsarraybuffer.ArrayBufferPrototype, default_attrs) +fill_prototype(ArrayBufferPrototype, jsarraybuffer.ArrayBufferPrototype, + default_attrs) # Int8Array proto -fill_prototype(Int8ArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Int8ArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) # Uint8Array proto -fill_prototype(Uint8ArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Uint8ArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) # Uint8ClampedArray proto -fill_prototype(Uint8ClampedArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Uint8ClampedArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) # Int16Array proto -fill_prototype(Int16ArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Int16ArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) # Uint16Array proto -fill_prototype(Uint16ArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Uint16ArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) # Int32Array proto -fill_prototype(Int32ArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Int32ArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) # Uint32Array proto -fill_prototype(Uint32ArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Uint32ArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) # Float32Array proto -fill_prototype(Float32ArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Float32ArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) # Float64Array proto -fill_prototype(Float64ArrayPrototype, jstypedarray.TypedArrayPrototype, default_attrs) +fill_prototype(Float64ArrayPrototype, jstypedarray.TypedArrayPrototype, + default_attrs) #Error proto fill_prototype(ErrorPrototype, jserror.ErrorPrototype, default_attrs) #RegExp proto @@ -2649,6 +3112,7 @@ def __proto__(val): ######################################################################### # Constructors + # String @Js def String(st): @@ -2656,6 +3120,7 @@ def String(st): return Js('') return arguments[0].to_string() + @Js def string_constructor(): temp = PyJsObject(prototype=StringPrototype) @@ -2665,22 +3130,36 @@ def string_constructor(): temp.value = '' else: temp.value = arguments[0].to_string().value - for i, ch in enumerate(temp.value): # this will make things long... - temp.own[str(i)] = {'value': Js(ch), 'writable': False, - 'enumerable': True, 'configurable': True} - temp.own['length'] = {'value': Js(len(temp.value)), 'writable': False, - 'enumerable': False, 'configurable': False} + for i, ch in enumerate(temp.value): # this will make things long... + temp.own[str(i)] = { + 'value': Js(ch), + 'writable': False, + 'enumerable': True, + 'configurable': True + } + temp.own['length'] = { + 'value': Js(len(temp.value)), + 'writable': False, + 'enumerable': False, + 'configurable': False + } return temp + String.create = string_constructor # RegExp REG_EXP_FLAGS = ('g', 'i', 'm') + + @Js def RegExp(pattern, flags): - if pattern.Class=='RegExp': + if pattern.Class == 'RegExp': if not flags.is_undefined(): - raise MakeError('TypeError', 'Cannot supply flags when constructing one RegExp from another') + raise MakeError( + 'TypeError', + 'Cannot supply flags when constructing one RegExp from another' + ) # return unchanged return pattern #pattern is not a regexp @@ -2695,17 +3174,23 @@ def RegExp(pattern, flags): flags = flags.to_string().value if not flags.is_undefined() else '' for flag in flags: if flag not in REG_EXP_FLAGS: - raise MakeError('SyntaxError', 'Invalid flags supplied to RegExp constructor "%s"' % flag) - if len(set(flags))!=len(flags): - raise MakeError('SyntaxError', 'Invalid flags supplied to RegExp constructor "%s"' % flags) - pattern = '/%s/'%(pattern if pattern else '(?:)') + flags + raise MakeError( + 'SyntaxError', + 'Invalid flags supplied to RegExp constructor "%s"' % flag) + if len(set(flags)) != len(flags): + raise MakeError( + 'SyntaxError', + 'Invalid flags supplied to RegExp constructor "%s"' % flags) + pattern = '/%s/' % (pattern if pattern else '(?:)') + flags return JsRegExp(pattern) + RegExp.create = RegExp PyJs.RegExp = RegExp # Number + @Js def Number(): if len(arguments): @@ -2713,6 +3198,7 @@ def Number(): else: return Js(0) + @Js def number_constructor(): temp = PyJsObject(prototype=NumberPrototype) @@ -2724,13 +3210,17 @@ def number_constructor(): temp.value = 0 return temp + Number.create = number_constructor # Boolean + @Js def Boolean(value): return value.to_boolean() + + @Js def boolean_constructor(value): temp = PyJsObject(prototype=BooleanPrototype) @@ -2739,11 +3229,12 @@ def boolean_constructor(value): temp.value = value.to_boolean().value return temp -Boolean.create = boolean_constructor +Boolean.create = boolean_constructor ############################################################################## + def appengine(code): try: return translator.translate_js(code.decode('utf-8')) @@ -2751,23 +3242,20 @@ def appengine(code): return traceback.format_exc() - -builtins = ('true','false','null','undefined','Infinity', - 'NaN') +builtins = ('true', 'false', 'null', 'undefined', 'Infinity', 'NaN') scope = dict(zip(builtins, [eval(e) for e in builtins])) -JS_BUILTINS = dict((k,v) for k,v in scope.items()) - +JS_BUILTINS = dict((k, v) for k, v in scope.items()) # Fill in NUM_BANK -for e in xrange(-2**10,2**14): +for e in xrange(-2**10, 2**14): NUM_BANK[e] = Js(e) -if __name__=='__main__': +if __name__ == '__main__': print(ObjectPrototype.get('toString').callprop('call')) print(FunctionPrototype.own) - a= null-Js(49404) + a = null - Js(49404) x = a.put('ser', Js('der')) print(Js(0) or Js('p') and Js(4.0000000000050000001)) FunctionPrototype.put('Chuj', Js(409)) @@ -2778,10 +3266,9 @@ def appengine(code): b = Js(6) s2 = Js(4) - o = ObjectPrototype + o = ObjectPrototype o.put('x', Js(100)) var = Scope(scope) e = code.InteractiveConsole(globals()) #e.raw_input = interactor e.interact() - diff --git a/js2py/constructors/jsarray.py b/js2py/constructors/jsarray.py index a6af010d..1550589d 100644 --- a/js2py/constructors/jsarray.py +++ b/js2py/constructors/jsarray.py @@ -1,38 +1,48 @@ from ..base import * + @Js def Array(): - if len(arguments)==0 or len(arguments)>1: + 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: + 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' + return arg.Class == 'Array' -Array.define_own_property('isArray', {'value': isArray, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +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}) +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}) \ No newline at end of file +ArrayPrototype.define_own_property('constructor', { + 'value': Array, + 'enumerable': False, + 'writable': True, + 'configurable': True +}) diff --git a/js2py/constructors/jsarraybuffer.py b/js2py/constructors/jsarraybuffer.py index 51468c06..6da71aab 100644 --- a/js2py/constructors/jsarraybuffer.py +++ b/js2py/constructors/jsarraybuffer.py @@ -14,21 +14,28 @@ def ArrayBuffer(): a = arguments[0] if isinstance(a, PyJsNumber): length = a.to_uint32() - if length!=a.value: + if length != a.value: raise MakeError('RangeError', 'Invalid array length') - temp = Js(bytearray([0]*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}) +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}) +ArrayBufferPrototype.define_own_property( + 'constructor', { + 'value': ArrayBuffer, + 'enumerable': False, + 'writable': False, + 'configurable': True + }) diff --git a/js2py/constructors/jsboolean.py b/js2py/constructors/jsboolean.py index 124e8fc6..743b110d 100644 --- a/js2py/constructors/jsboolean.py +++ b/js2py/constructors/jsboolean.py @@ -1,11 +1,16 @@ from ..base import * -BooleanPrototype.define_own_property('constructor', {'value': Boolean, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +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}) \ No newline at end of file +Boolean.define_own_property( + 'prototype', { + 'value': BooleanPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) diff --git a/js2py/constructors/jsdate.py b/js2py/constructors/jsdate.py index d88fbd77..98de6431 100644 --- a/js2py/constructors/jsdate.py +++ b/js2py/constructors/jsdate.py @@ -1,60 +1,74 @@ from ..base import * from .time_helpers import * -TZ_OFFSET = (time.altzone//3600) +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) + return PyJsDate(int(time.time() * 1000), prototype=DatePrototype) @Js -def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this +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: + 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) + 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('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('parse', {'value': parse, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +Date.define_own_property('UTC', { + 'value': UTC, + '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 = {} @@ -62,10 +76,11 @@ def __init__(self, value, prototype=None): # todo fix this problematic datetime part def to_local_dt(self): - return datetime.datetime.utcfromtimestamp(UTCToLocal(self.value)//1000) + return datetime.datetime.utcfromtimestamp( + UTCToLocal(self.value) // 1000) def to_utc_dt(self): - return datetime.datetime.utcfromtimestamp(self.value//1000) + return datetime.datetime.utcfromtimestamp(self.value // 1000) def local_strftime(self, pattern): if self.value is NaN: @@ -73,11 +88,16 @@ def local_strftime(self, pattern): try: dt = self.to_local_dt() except: - raise MakeError('TypeError', 'unsupported date range. Will fix in future versions') + 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)') + 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: @@ -85,31 +105,40 @@ def utc_strftime(self, pattern): try: dt = self.to_utc_dt() except: - raise MakeError('TypeError', 'unsupported date range. Will fix in future versions') + 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)') + 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 +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))) + 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) - + 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: + if len(args) >= 2: return date_constructor2(*args) - elif len(args)==1: + elif len(args) == 1: return date_constructor1(args[0]) else: return date_constructor0() @@ -121,7 +150,7 @@ def date_constructor0(): def date_constructor1(value): v = value.to_primitive() - if v._type()=='String': + if v._type() == 'String': v = parse_date(v.value) else: v = v.to_int() @@ -132,22 +161,25 @@ 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: + 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)))) + 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': + if obj.Class != 'Date': raise MakeError('TypeError', 'this is not a Date object') @@ -156,8 +188,10 @@ 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)) + 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) @@ -287,8 +321,7 @@ def getTimezoneOffset(): check_date(this) if this.value is NaN: return NaN - return (this.value - UTCToLocal(this.value))//60000 - + return (this.value - UTCToLocal(this.value)) // 60000 def setTime(time): check_date(this) @@ -298,7 +331,8 @@ def setTime(time): def setMilliseconds(ms): check_date(this) t = UTCToLocal(this.value) - tim = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) + tim = MakeTime( + HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) u = TimeClip(LocalToUTC(MakeDate(Day(t), tim))) this.value = u return u @@ -306,7 +340,8 @@ def setMilliseconds(ms): def setUTCMilliseconds(ms): check_date(this) t = this.value - tim = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) + tim = MakeTime( + HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) u = TimeClip(MakeDate(Day(t), tim)) this.value = u return u @@ -321,13 +356,17 @@ 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) + 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(): + if tv.Class == 'Number' and not tv.is_finite(): return this.null toISO = o.get('toISOString') if not toISO.is_callable(): @@ -338,34 +377,29 @@ def toJSON(key): def pad(num, n=2, sign=False): '''returns n digit string representation of the num''' s = unicode(abs(num)) - if len(s)=0: - return '+'+s + if num >= 0: + return '+' + s else: - return '-'+s - - - - - - - - + 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}) \ No newline at end of file +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 +}) diff --git a/js2py/constructors/jsfloat32array.py b/js2py/constructors/jsfloat32array.py index 95e2662e..3a121916 100644 --- a/js2py/constructors/jsfloat32array.py +++ b/js2py/constructors/jsfloat32array.py @@ -9,39 +9,48 @@ @Js def Float32Array(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + 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) + 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) + 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] + 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) + 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') + 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') + 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) + 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 @@ -50,20 +59,29 @@ def Float32Array(): 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}) +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( + '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}) +Float32ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(4), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/jsfloat64array.py b/js2py/constructors/jsfloat64array.py index 3e5a816a..9ea5f052 100644 --- a/js2py/constructors/jsfloat64array.py +++ b/js2py/constructors/jsfloat64array.py @@ -9,39 +9,48 @@ @Js def Float64Array(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + 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) + 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) + 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] + 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) + 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') + 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') + 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) + 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 @@ -50,20 +59,29 @@ def Float64Array(): 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}) +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( + '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}) +Float64ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(8), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/jsfunction.py b/js2py/constructors/jsfunction.py index 9a8c4b25..c4ace2c3 100644 --- a/js2py/constructors/jsfunction.py +++ b/js2py/constructors/jsfunction.py @@ -17,18 +17,19 @@ def Function(): # 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, '') + 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.source = '{%s}' % body temp.func_name = 'anonymous' return temp + def executor(f, glob): - exec(f, globals()) + exec (f, globals()) return globals()['PyJs_anonymous_0_'] @@ -37,13 +38,15 @@ def executor(f, glob): #set constructor property inside FunctionPrototype -fill_in_props(FunctionPrototype, {'constructor':Function}, default_attrs) +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}) +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) - diff --git a/js2py/constructors/jsint16array.py b/js2py/constructors/jsint16array.py index 8641690d..c2864105 100644 --- a/js2py/constructors/jsint16array.py +++ b/js2py/constructors/jsint16array.py @@ -6,41 +6,51 @@ except: pass + @Js def Int16Array(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + 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) + 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) + 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] + 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) + 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') + 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') + 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) + 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 @@ -49,20 +59,29 @@ def Int16Array(): 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}) +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( + '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}) +Int16ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(2), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/jsint32array.py b/js2py/constructors/jsint32array.py index 3f50ded1..2e8c4e84 100644 --- a/js2py/constructors/jsint32array.py +++ b/js2py/constructors/jsint32array.py @@ -9,39 +9,48 @@ @Js def Int32Array(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + 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) + 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) + 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] + 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) + 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') + 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') + 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) + 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 @@ -50,20 +59,29 @@ def Int32Array(): 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}) +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( + '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}) +Int32ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(4), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/jsint8array.py b/js2py/constructors/jsint8array.py index 68adc468..3396afea 100644 --- a/js2py/constructors/jsint8array.py +++ b/js2py/constructors/jsint8array.py @@ -6,28 +6,33 @@ except: pass + @Js def Int8Array(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + 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) + 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) + 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] + 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) + elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc) if len(arguments) > 1: offset = int(arguments[1].value) else: @@ -35,8 +40,9 @@ def Int8Array(): 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) + 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 @@ -45,20 +51,29 @@ def Int8Array(): 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}) +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( + '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}) +Int8ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(1), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/jsmath.py b/js2py/constructors/jsmath.py index 8774527a..06751a3b 100644 --- a/js2py/constructors/jsmath.py +++ b/js2py/constructors/jsmath.py @@ -5,31 +5,37 @@ 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} +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}) + 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 + 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 + if a != a: # it must be a nan return NaN try: return math.acos(a) @@ -38,7 +44,7 @@ def acos(x): def asin(x): a = x.to_number().value - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN try: return math.asin(a) @@ -47,56 +53,56 @@ def asin(x): def atan(x): a = x.to_number().value - if a!=a: # it must be a nan + 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 + 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 + 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 + 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 + 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 + 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 + if a != a: # it must be a nan return NaN - return math.cos(a) + return math.cos(a) def tan(x): a = x.to_number().value - if a!=a: # it must be a nan + 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 + if a != a: # it must be a nan return NaN try: return math.log(a) @@ -105,14 +111,14 @@ def log(x): def exp(x): a = x.to_number().value - if a!=a: # it must be a nan + 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 + if a != a or b != b: # it must be a nan return NaN try: return a**b @@ -121,7 +127,7 @@ def pow(x, y): def sqrt(x): a = x.to_number().value - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN try: return a**0.5 @@ -132,7 +138,7 @@ 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 + if any(e != e for e in lis): # we dont want NaNs return NaN return min(*lis) @@ -140,7 +146,7 @@ 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 + if any(e != e for e in lis): # we dont want NaNs return NaN return max(*lis) @@ -148,4 +154,4 @@ def random(): return random.random() -fill_prototype(Math, MathFunctions, default_attrs) \ No newline at end of file +fill_prototype(Math, MathFunctions, default_attrs) diff --git a/js2py/constructors/jsnumber.py b/js2py/constructors/jsnumber.py index 9acb43a0..9ba7c7c7 100644 --- a/js2py/constructors/jsnumber.py +++ b/js2py/constructors/jsnumber.py @@ -1,18 +1,23 @@ 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') +} -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 +}) -fill_in_props(Number, CONSTS, {'enumerable': False, - 'writable': False, - 'configurable': False}) - -NumberPrototype.define_own_property('constructor', {'value': Number, - 'enumerable': False, - 'writable': True, - 'configurable': True}) \ No newline at end of file +NumberPrototype.define_own_property('constructor', { + 'value': Number, + 'enumerable': False, + 'writable': True, + 'configurable': True +}) diff --git a/js2py/constructors/jsobject.py b/js2py/constructors/jsobject.py index 5169fe1a..c4e0ada3 100644 --- a/js2py/constructors/jsobject.py +++ b/js2py/constructors/jsobject.py @@ -3,6 +3,7 @@ #todo Double check everything is OK + @Js def Object(): val = arguments.get('0') @@ -15,13 +16,14 @@ def Object(): def object_constructor(): if len(arguments): val = arguments.get('0') - if val.TYPE=='Object': + if val.TYPE == 'Object': #Implementation dependent, but my will simply return :) return val elif val.TYPE in ('Number', 'String', 'Boolean'): return val.to_object() return PyJsObject(prototype=ObjectPrototype) + Object.create = object_constructor Object.own['length']['value'] = Js(1) @@ -29,24 +31,32 @@ def object_constructor(): class ObjectMethods: def getPrototypeOf(obj): if not obj.is_object(): - raise MakeError('TypeError', 'Object.getPrototypeOf called on non-object') + raise MakeError('TypeError', + 'Object.getPrototypeOf called on non-object') return null if obj.prototype is None else obj.prototype - def getOwnPropertyDescriptor (obj, prop): + def getOwnPropertyDescriptor(obj, prop): if not obj.is_object(): - raise MakeError('TypeError', 'Object.getOwnPropertyDescriptor called on non-object') - return obj.own.get(prop.to_string().value) # will return undefined if we dont have this prop + raise MakeError( + 'TypeError', + 'Object.getOwnPropertyDescriptor called on non-object') + return obj.own.get( + prop.to_string(). + value) # will return undefined if we dont have this prop def getOwnPropertyNames(obj): if not obj.is_object(): - raise MakeError('TypeError', 'Object.getOwnPropertyDescriptor called on non-object') + raise MakeError( + 'TypeError', + 'Object.getOwnPropertyDescriptor called on non-object') return obj.own.keys() def create(obj): if not (obj.is_object() or obj.is_null()): - raise MakeError('TypeError', 'Object prototype may only be an Object or null') + raise MakeError('TypeError', + 'Object prototype may only be an Object or null') temp = PyJsObject(prototype=(None if obj.is_null() else obj)) - if len(arguments)>1 and not arguments[1].is_undefined(): + if len(arguments) > 1 and not arguments[1].is_undefined(): if six.PY2: ObjectMethods.defineProperties.__func__(temp, arguments[1]) else: @@ -55,7 +65,8 @@ def create(obj): def defineProperty(obj, prop, attrs): if not obj.is_object(): - raise MakeError('TypeError', 'Object.defineProperty called on non-object') + raise MakeError('TypeError', + 'Object.defineProperty called on non-object') name = prop.to_string().value if not obj.define_own_property(name, ToPropertyDescriptor(attrs)): raise MakeError('TypeError', 'Cannot redefine property: %s' % name) @@ -63,12 +74,15 @@ def defineProperty(obj, prop, attrs): def defineProperties(obj, properties): if not obj.is_object(): - raise MakeError('TypeError', 'Object.defineProperties called on non-object') + raise MakeError('TypeError', + 'Object.defineProperties called on non-object') props = properties.to_object() for name in props: desc = ToPropertyDescriptor(props.get(name.value)) if not obj.define_own_property(name.value, desc): - raise MakeError('TypeError', 'Failed to define own property: %s'%name.value) + raise MakeError( + 'TypeError', + 'Failed to define own property: %s' % name.value) return obj def seal(obj): @@ -91,13 +105,15 @@ def freeze(obj): def preventExtensions(obj): if not obj.is_object(): - raise MakeError('TypeError', 'Object.preventExtensions on non-object') + raise MakeError('TypeError', + 'Object.preventExtensions on non-object') obj.extensible = False return obj def isSealed(obj): if not obj.is_object(): - raise MakeError('TypeError', 'Object.isSealed called on non-object') + raise MakeError('TypeError', + 'Object.isSealed called on non-object') if obj.extensible: return False for desc in obj.own.values(): @@ -107,7 +123,8 @@ def isSealed(obj): def isFrozen(obj): if not obj.is_object(): - raise MakeError('TypeError', 'Object.isFrozen called on non-object') + raise MakeError('TypeError', + 'Object.isFrozen called on non-object') if obj.extensible: return False for desc in obj.own.values(): @@ -119,32 +136,36 @@ def isFrozen(obj): def isExtensible(obj): if not obj.is_object(): - raise MakeError('TypeError', 'Object.isExtensible called on non-object') + raise MakeError('TypeError', + 'Object.isExtensible called on non-object') return obj.extensible def keys(obj): if not obj.is_object(): raise MakeError('TypeError', 'Object.keys called on non-object') - return [e for e,d in six.iteritems(obj.own) if d.get('enumerable')] + return [e for e, d in six.iteritems(obj.own) if d.get('enumerable')] # add methods attached to Object constructor fill_prototype(Object, ObjectMethods, default_attrs) # add constructor to prototype -fill_in_props(ObjectPrototype, {'constructor':Object}, default_attrs) +fill_in_props(ObjectPrototype, {'constructor': Object}, default_attrs) # add prototype property to the constructor. -Object.define_own_property('prototype', {'value': ObjectPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) - - +Object.define_own_property( + 'prototype', { + 'value': ObjectPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) # some utility functions: + def ToPropertyDescriptor(obj): # page 38 (50 absolute) - if obj.TYPE!='Object': - raise MakeError('TypeError', 'Can\'t convert non-object to property descriptor') + if obj.TYPE != 'Object': + raise MakeError('TypeError', + 'Can\'t convert non-object to property descriptor') desc = {} if obj.has_property('enumerable'): desc['enumerable'] = obj.get('enumerable').to_boolean().value @@ -155,18 +176,23 @@ def ToPropertyDescriptor(obj): # page 38 (50 absolute) if obj.has_property('writable'): desc['writable'] = obj.get('writable').to_boolean().value if obj.has_property('get'): - cand = obj.get('get') + cand = obj.get('get') if not (cand.is_undefined() or cand.is_callable()): - raise MakeError('TypeError', 'Invalid getter (it has to be a function or undefined)') + raise MakeError( + 'TypeError', + 'Invalid getter (it has to be a function or undefined)') desc['get'] = cand if obj.has_property('set'): - cand = obj.get('set') + cand = obj.get('set') if not (cand.is_undefined() or cand.is_callable()): - raise MakeError('TypeError', 'Invalid setter (it has to be a function or undefined)') + raise MakeError( + 'TypeError', + 'Invalid setter (it has to be a function or undefined)') desc['set'] = cand - if ('get' in desc or 'set' in desc) and ('value' in desc or 'writable' in desc): - raise MakeError('TypeError', 'Invalid property. A property cannot both have accessors and be writable or have a value.') + if ('get' in desc or 'set' in desc) and ('value' in desc + or 'writable' in desc): + raise MakeError( + 'TypeError', + 'Invalid property. A property cannot both have accessors and be writable or have a value.' + ) return desc - - - diff --git a/js2py/constructors/jsregexp.py b/js2py/constructors/jsregexp.py index 120c097d..2dfc25cb 100644 --- a/js2py/constructors/jsregexp.py +++ b/js2py/constructors/jsregexp.py @@ -1,11 +1,16 @@ from ..base import * -RegExpPrototype.define_own_property('constructor', {'value': RegExp, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +RegExpPrototype.define_own_property('constructor', { + 'value': RegExp, + 'enumerable': False, + 'writable': True, + 'configurable': True +}) -RegExp.define_own_property('prototype', {'value': RegExpPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) \ No newline at end of file +RegExp.define_own_property( + 'prototype', { + 'value': RegExpPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) diff --git a/js2py/constructors/jsstring.py b/js2py/constructors/jsstring.py index cf9100af..0650d0f5 100644 --- a/js2py/constructors/jsstring.py +++ b/js2py/constructors/jsstring.py @@ -4,27 +4,37 @@ if six.PY3: unichr = chr + @Js def fromCharCode(): args = arguments.to_list() res = u'' for e in args: - res +=unichr(e.to_uint16()) + res += unichr(e.to_uint16()) return this.Js(res) + fromCharCode.own['length']['value'] = Js(1) -String.define_own_property('fromCharCode', {'value': fromCharCode, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +String.define_own_property( + 'fromCharCode', { + 'value': fromCharCode, + 'enumerable': False, + 'writable': True, + 'configurable': True + }) -String.define_own_property('prototype', {'value': StringPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) +String.define_own_property( + 'prototype', { + 'value': StringPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) -StringPrototype.define_own_property('constructor', {'value': String, - 'enumerable': False, - 'writable': True, - 'configurable': True}) \ No newline at end of file +StringPrototype.define_own_property('constructor', { + 'value': String, + 'enumerable': False, + 'writable': True, + 'configurable': True +}) diff --git a/js2py/constructors/jsuint16array.py b/js2py/constructors/jsuint16array.py index efc4f67d..a61be246 100644 --- a/js2py/constructors/jsuint16array.py +++ b/js2py/constructors/jsuint16array.py @@ -6,41 +6,51 @@ except: pass + @Js def Uint16Array(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + if length != a.value: raise MakeError('RangeError', 'Invalid array length') temp = Js(numpy.full(length, 0, dtype=numpy.uint16)) temp.put('length', a) return temp - elif isinstance(a, PyJsString): # object (string) + elif isinstance(a, PyJsString): # object (string) temp = Js(numpy.array(list(a.value), dtype=numpy.uint16)) 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) + 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] + array = [(int(item.value) if item.value != None else 0) + for item in array] temp = Js(numpy.array(array, dtype=numpy.uint16)) temp.put('length', Js(len(array))) return temp - elif isinstance(a,PyObjectWrapper): # object (ArrayBuffer, etc) + elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc) if len(a.obj) % 2 != 0: - raise MakeError('RangeError', 'Byte length of Uint16Array should be a multiple of 2') + raise MakeError( + 'RangeError', + 'Byte length of Uint16Array 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 Uint16Array should be a multiple of 2') + raise MakeError( + 'RangeError', + 'Start offset of Uint16Array 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.uint16, count=length, offset=offset) + length = int((len(a.obj) - offset) / 2) + array = numpy.frombuffer( + a.obj, dtype=numpy.uint16, count=length, offset=offset) temp = Js(array) temp.put('length', Js(length)) temp.buff = array @@ -49,20 +59,29 @@ def Uint16Array(): temp.put('length', Js(0)) return temp + Uint16Array.create = Uint16Array Uint16Array.own['length']['value'] = Js(3) -Uint16Array.define_own_property('prototype', {'value': Uint16ArrayPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Uint16Array.define_own_property( + 'prototype', { + 'value': Uint16ArrayPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) -Uint16ArrayPrototype.define_own_property('constructor', {'value': Uint16Array, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +Uint16ArrayPrototype.define_own_property( + 'constructor', { + 'value': Uint16Array, + 'enumerable': False, + 'writable': True, + 'configurable': True + }) -Uint16ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {'value': Js(2), - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Uint16ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(2), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/jsuint32array.py b/js2py/constructors/jsuint32array.py index b9f402cf..c6f57c94 100644 --- a/js2py/constructors/jsuint32array.py +++ b/js2py/constructors/jsuint32array.py @@ -6,24 +6,29 @@ except: pass + @Js def Uint32Array(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + if length != a.value: raise MakeError('RangeError', 'Invalid array length') temp = Js(numpy.full(length, 0, dtype=numpy.uint32)) temp.put('length', a) return temp - elif isinstance(a, PyJsString): # object (string) + elif isinstance(a, PyJsString): # object (string) temp = Js(numpy.array(list(a.value), dtype=numpy.uint32)) 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) + 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] + array = [(int(item.value) if item.value != None else 0) + for item in array] if len(arguments) > 1: offset = int(arguments[1].value) else: @@ -31,44 +36,60 @@ def Uint32Array(): if len(arguments) > 2: length = int(arguments[2].value) else: - length = len(array)-offset - temp = Js(numpy.array(array[offset:offset+length], dtype=numpy.uint32)) + length = len(array) - offset + temp = Js( + numpy.array(array[offset:offset + length], dtype=numpy.uint32)) temp.put('length', Js(length)) return temp - elif isinstance(a,PyObjectWrapper): # object (ArrayBuffer, etc) + elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc) if len(a.obj) % 4 != 0: - raise MakeError('RangeError', 'Byte length of Uint32Array should be a multiple of 4') + raise MakeError( + 'RangeError', + 'Byte length of Uint32Array 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 Uint32Array should be a multiple of 4') + raise MakeError( + 'RangeError', + 'Start offset of Uint32Array 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) - temp = Js(numpy.frombuffer(a.obj, dtype=numpy.uint32, count=length, offset=offset)) + length = int((len(a.obj) - offset) / 4) + temp = Js( + numpy.frombuffer( + a.obj, dtype=numpy.uint32, count=length, offset=offset)) temp.put('length', Js(length)) return temp temp = Js(numpy.full(0, 0, dtype=numpy.uint32)) temp.put('length', Js(0)) return temp + Uint32Array.create = Uint32Array Uint32Array.own['length']['value'] = Js(3) -Uint32Array.define_own_property('prototype', {'value': Uint32ArrayPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Uint32Array.define_own_property( + 'prototype', { + 'value': Uint32ArrayPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) -Uint32ArrayPrototype.define_own_property('constructor', {'value': Uint32Array, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +Uint32ArrayPrototype.define_own_property( + 'constructor', { + 'value': Uint32Array, + 'enumerable': False, + 'writable': True, + 'configurable': True + }) -Uint32ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {'value': Js(4), - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Uint32ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(4), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/jsuint8array.py b/js2py/constructors/jsuint8array.py index a0f5df01..4b2199da 100644 --- a/js2py/constructors/jsuint8array.py +++ b/js2py/constructors/jsuint8array.py @@ -9,26 +9,30 @@ @Js def Uint8Array(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + if length != a.value: raise MakeError('RangeError', 'Invalid array length') temp = Js(numpy.full(length, 0, dtype=numpy.uint8)) temp.put('length', a) return temp - elif isinstance(a, PyJsString): # object (string) + elif isinstance(a, PyJsString): # object (string) temp = Js(numpy.array(list(a.value), dtype=numpy.uint8)) 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) + 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] + array = [(int(item.value) if item.value != None else 0) + for item in array] temp = Js(numpy.array(array, dtype=numpy.uint8)) temp.put('length', Js(len(array))) return temp - elif isinstance(a,PyObjectWrapper): # object (ArrayBuffer, etc) + elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc) if len(arguments) > 1: offset = int(arguments[1].value) else: @@ -36,8 +40,9 @@ def Uint8Array(): if len(arguments) > 2: length = int(arguments[2].value) else: - length = int(len(a.obj)-offset) - array = numpy.frombuffer(a.obj, dtype=numpy.uint8, count=length, offset=offset) + length = int(len(a.obj) - offset) + array = numpy.frombuffer( + a.obj, dtype=numpy.uint8, count=length, offset=offset) temp = Js(array) temp.put('length', Js(length)) temp.buff = array @@ -46,20 +51,29 @@ def Uint8Array(): temp.put('length', Js(0)) return temp + Uint8Array.create = Uint8Array Uint8Array.own['length']['value'] = Js(3) -Uint8Array.define_own_property('prototype', {'value': Uint8ArrayPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Uint8Array.define_own_property( + 'prototype', { + 'value': Uint8ArrayPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) -Uint8ArrayPrototype.define_own_property('constructor', {'value': Uint8Array, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +Uint8ArrayPrototype.define_own_property( + 'constructor', { + 'value': Uint8Array, + 'enumerable': False, + 'writable': True, + 'configurable': True + }) -Uint8ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {'value': Js(1), - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Uint8ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(1), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/jsuint8clampedarray.py b/js2py/constructors/jsuint8clampedarray.py index 973e38ef..7b5e350b 100644 --- a/js2py/constructors/jsuint8clampedarray.py +++ b/js2py/constructors/jsuint8clampedarray.py @@ -9,26 +9,30 @@ @Js def Uint8ClampedArray(): - TypedArray = (PyJsInt8Array,PyJsUint8Array,PyJsUint8ClampedArray,PyJsInt16Array,PyJsUint16Array,PyJsInt32Array,PyJsUint32Array,PyJsFloat32Array,PyJsFloat64Array) + TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray, + PyJsInt16Array, PyJsUint16Array, PyJsInt32Array, + PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array) a = arguments[0] - if isinstance(a, PyJsNumber): # length + if isinstance(a, PyJsNumber): # length length = a.to_uint32() - if length!=a.value: + if length != a.value: raise MakeError('RangeError', 'Invalid array length') temp = Js(numpy.full(length, 0, dtype=numpy.uint8), Clamped=True) temp.put('length', a) return temp - elif isinstance(a, PyJsString): # object (string) + elif isinstance(a, PyJsString): # object (string) temp = Js(numpy.array(list(a.value), dtype=numpy.uint8), Clamped=True) 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) + 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] + array = [(int(item.value) if item.value != None else 0) + for item in array] temp = Js(numpy.array(array, dtype=numpy.uint8), Clamped=True) temp.put('length', Js(len(array))) return temp - elif isinstance(a,PyObjectWrapper): # object (ArrayBuffer, etc) + elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc) if len(arguments) > 1: offset = int(arguments[1].value) else: @@ -36,8 +40,9 @@ def Uint8ClampedArray(): if len(arguments) > 2: length = int(arguments[2].value) else: - length = int(len(a.obj)-offset) - array = numpy.frombuffer(a.obj, dtype=numpy.uint8, count=length, offset=offset) + length = int(len(a.obj) - offset) + array = numpy.frombuffer( + a.obj, dtype=numpy.uint8, count=length, offset=offset) temp = Js(array, Clamped=True) temp.put('length', Js(length)) temp.buff = array @@ -46,20 +51,29 @@ def Uint8ClampedArray(): temp.put('length', Js(0)) return temp + Uint8ClampedArray.create = Uint8ClampedArray Uint8ClampedArray.own['length']['value'] = Js(3) -Uint8ClampedArray.define_own_property('prototype', {'value': Uint8ClampedArrayPrototype, - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Uint8ClampedArray.define_own_property( + 'prototype', { + 'value': Uint8ClampedArrayPrototype, + 'enumerable': False, + 'writable': False, + 'configurable': False + }) -Uint8ClampedArrayPrototype.define_own_property('constructor', {'value': Uint8ClampedArray, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +Uint8ClampedArrayPrototype.define_own_property( + 'constructor', { + 'value': Uint8ClampedArray, + 'enumerable': False, + 'writable': True, + 'configurable': True + }) -Uint8ClampedArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {'value': Js(1), - 'enumerable': False, - 'writable': False, - 'configurable': False}) +Uint8ClampedArrayPrototype.define_own_property('BYTES_PER_ELEMENT', { + 'value': Js(1), + 'enumerable': False, + 'writable': False, + 'configurable': False +}) diff --git a/js2py/constructors/time_helpers.py b/js2py/constructors/time_helpers.py index 493ac726..a744d8ca 100644 --- a/js2py/constructors/time_helpers.py +++ b/js2py/constructors/time_helpers.py @@ -6,17 +6,21 @@ try: from tzlocal import get_localzone LOCAL_ZONE = get_localzone() -except: # except all problems... - warnings.warn('Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time') +except: # except all problems... + warnings.warn( + 'Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time' + ) + class LOCAL_ZONE: @staticmethod def dst(*args): return 1 + from js2py.base import MakeError CUM = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) msPerDay = 86400000 -msPerYear = int(86400000*365.242) +msPerYear = int(86400000 * 365.242) msPerSecond = 1000 msPerMinute = 60000 msPerHour = 3600000 @@ -24,66 +28,74 @@ def dst(*args): MinutesPerHour = 60 SecondsPerMinute = 60 NaN = float('nan') -LocalTZA = - time.timezone * msPerSecond - - - +LocalTZA = -time.timezone * msPerSecond def DaylightSavingTA(t): if t is NaN: return t try: - return int(LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(t//1000)).seconds)*1000 + return int( + LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp( + t // 1000)).seconds) * 1000 except: - warnings.warn('Invalid datetime date, assumed DST time, may be inaccurate...', Warning) + warnings.warn( + 'Invalid datetime date, assumed DST time, may be inaccurate...', + Warning) return 1 #raise MakeError('TypeError', 'date not supported by python.datetime. I will solve it in future versions') + def GetTimeZoneName(t): - return time.tzname[DaylightSavingTA(t)>0] + return time.tzname[DaylightSavingTA(t) > 0] + def LocalToUTC(t): return t - LocalTZA - DaylightSavingTA(t - LocalTZA) + def UTCToLocal(t): return t + LocalTZA + DaylightSavingTA(t) def Day(t): - return t//86400000 + return t // 86400000 def TimeWithinDay(t): - return t%86400000 + return t % 86400000 + def DaysInYear(y): - if y%4: + if y % 4: return 365 - elif y%100: + elif y % 100: return 366 - elif y%400: + elif y % 400: return 365 else: return 366 def DayFromYear(y): - return 365 * (y-1970) + (y-1969)//4 -(y-1901)//100 + (y-1601)//400 + return 365 * (y - 1970) + (y - 1969) // 4 - (y - 1901) // 100 + ( + y - 1601) // 400 + def TimeFromYear(y): return 86400000 * DayFromYear(y) + def YearFromTime(t): - guess = 1970 - t//31556908800 # msPerYear + guess = 1970 - t // 31556908800 # msPerYear gt = TimeFromYear(guess) - if gt<=t: - while gt<=t: + if gt <= t: + while gt <= t: guess += 1 gt = TimeFromYear(guess) - return guess-1 + return guess - 1 else: - while gt>t: + while gt > t: guess -= 1 gt = TimeFromYear(guess) return guess @@ -92,73 +104,83 @@ def YearFromTime(t): def DayWithinYear(t): return Day(t) - DayFromYear(YearFromTime(t)) + def InLeapYear(t): y = YearFromTime(t) - if y%4: + if y % 4: return 0 - elif y%100: + elif y % 100: return 1 - elif y%400: + elif y % 400: return 0 else: return 1 + def MonthFromTime(t): day = DayWithinYear(t) leap = InLeapYear(t) - if day<31: + if day < 31: return 0 day -= leap - if day<59: + if day < 59: return 1 - elif day<90: + elif day < 90: return 2 - elif day<120: + elif day < 120: return 3 - elif day<151: + elif day < 151: return 4 - elif day<181: + elif day < 181: return 5 - elif day<212: + elif day < 212: return 6 - elif day<243: + elif day < 243: return 7 - elif day<273: + elif day < 273: return 8 - elif day<304: + elif day < 304: return 9 - elif day<334: + elif day < 334: return 10 else: return 11 + def DateFromTime(t): mon = MonthFromTime(t) day = DayWithinYear(t) - return day-CUM[mon] - (1 if InLeapYear(t) and mon>=2 else 0) + 1 + return day - CUM[mon] - (1 if InLeapYear(t) and mon >= 2 else 0) + 1 + def WeekDay(t): # 0 == sunday return (Day(t) + 4) % 7 + def msFromTime(t): return t % 1000 + def SecFromTime(t): - return (t//1000) % 60 + return (t // 1000) % 60 + def MinFromTime(t): - return (t//60000) % 60 + return (t // 60000) % 60 + def HourFromTime(t): - return (t//3600000) % 24 + return (t // 3600000) % 24 + -def MakeTime (hour, Min, sec, ms): +def MakeTime(hour, Min, sec, ms): # takes PyJs objects and returns t - if not (hour.is_finite() and Min.is_finite() and sec.is_finite() and ms.is_finite()): + if not (hour.is_finite() and Min.is_finite() and sec.is_finite() + and ms.is_finite()): return NaN h, m, s, milli = hour.to_int(), Min.to_int(), sec.to_int(), ms.to_int() - return h*3600000 + m*60000 + s*1000 + milli + return h * 3600000 + m * 60000 + s * 1000 + milli def MakeDay(year, month, date): @@ -166,19 +188,20 @@ def MakeDay(year, month, date): if not (year.is_finite() and month.is_finite() and date.is_finite()): return NaN y, m, dt = year.to_int(), month.to_int(), date.to_int() - y += m//12 + y += m // 12 mn = m % 12 - d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y)==366 and mn>=2 else 0) - return d # ms per day + d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y) == 366 + and mn >= 2 else 0) + return d # ms per day + def MakeDate(day, time): - return 86400000*day + time + return 86400000 * day + time def TimeClip(t): - if t!=t or abs(t)==float('inf'): + if t != t or abs(t) == float('inf'): return NaN - if abs(t) > 8.64 *10**15: + if abs(t) > 8.64 * 10**15: return NaN return int(t) - diff --git a/js2py/es6/__init__.py b/js2py/es6/__init__.py index 45c4a5dc..4a58d3f0 100644 --- a/js2py/es6/__init__.py +++ b/js2py/es6/__init__.py @@ -2,11 +2,14 @@ babel = None babelPresetEs2015 = None + def js6_to_js5(code): global INITIALISED, babel, babelPresetEs2015 if not INITIALISED: import signal, warnings, time - warnings.warn('\nImporting babel.py for the first time - this can take some time. \nPlease note that currently Javascript 6 in Js2Py is unstable and slow. Use only for tiny scripts!') + warnings.warn( + '\nImporting babel.py for the first time - this can take some time. \nPlease note that currently Javascript 6 in Js2Py is unstable and slow. Use only for tiny scripts!' + ) from .babel import babel as _babel babel = _babel.Object.babel @@ -16,9 +19,14 @@ def js6_to_js5(code): try: babel.transform('warmup', {'presets': {}}) signal.alarm(2) - def kill_it(a,b): raise KeyboardInterrupt('Better work next time!') + + def kill_it(a, b): + raise KeyboardInterrupt('Better work next time!') + signal.signal(signal.SIGALRM, kill_it) - babel.transform('stuckInALoop', {'presets': babelPresetEs2015}).code + babel.transform('stuckInALoop', { + 'presets': babelPresetEs2015 + }).code for n in range(3): time.sleep(1) except: @@ -26,7 +34,8 @@ def kill_it(a,b): raise KeyboardInterrupt('Better work next time!') INITIALISED = True return babel.transform(code, {'presets': babelPresetEs2015}).code -if __name__=='__main__': + +if __name__ == '__main__': print(js6_to_js5('obj={}; obj.x = function() {return () => this}')) print() - print(js6_to_js5('const a = 1;')) \ No newline at end of file + print(js6_to_js5('const a = 1;')) diff --git a/js2py/evaljs.py b/js2py/evaljs.py index 1d324866..b67faf61 100644 --- a/js2py/evaljs.py +++ b/js2py/evaljs.py @@ -9,21 +9,26 @@ 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'] +__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()""" @@ -51,6 +56,7 @@ def write_file_contents(path_or_file, contents): 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. @@ -68,16 +74,13 @@ def translate_file(input_path, output_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) + 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). ''' @@ -89,7 +92,6 @@ def run_file(path_or_file, context=None): return eval_value, context - def eval_js(js): """Just like javascript eval. Translates javascript to python, executes and returns python object. @@ -112,6 +114,7 @@ def eval_js(js): e = EvalJs() return e.eval(js) + def eval_js6(js): return eval_js(js6_to_js5(js)) @@ -120,7 +123,6 @@ def translate_js6(js): return translate_js(js6_to_js5(js)) - class EvalJs(object): """This class supports continuous execution of javascript under same context. @@ -139,15 +141,17 @@ class EvalJs(object): 30 You can run interactive javascript console with console method!""" + def __init__(self, context={}): self.__dict__['_context'] = {} - exec(DEFAULT_HEADER, self._context) + exec (DEFAULT_HEADER, self._context) self.__dict__['_var'] = self._context['var'].to_python() if not isinstance(context, dict): try: context = context.__dict__ except: - raise TypeError('context has to be either a dict or have __dict__ attr') + 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) @@ -172,13 +176,15 @@ def execute(self, js=None, use_compilation_plan=False): try: compiled = cache[hashkey] except KeyError: - code = translate_js(js, '', use_compilation_plan=use_compilation_plan) - compiled = cache[hashkey] = compile(code, '', 'exec') - exec(compiled, self._context) + code = translate_js( + js, '', use_compilation_plan=use_compilation_plan) + compiled = cache[hashkey] = compile(code, '', + '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) + code = 'PyJsEvalResult = eval(%s)' % json.dumps(expression) self.execute(code, use_compilation_plan=use_compilation_plan) return self['PyJsEvalResult'] @@ -189,7 +195,8 @@ def execute_debug(self, js): """ code = translate_js(js, '') # make sure you have a temp folder: - filename = 'temp' + os.sep + '_' + hashlib.md5(code).hexdigest() + '.py' + filename = 'temp' + os.sep + '_' + hashlib.md5( + code).hexdigest() + '.py' try: with open(filename, mode='w') as f: f.write(code) @@ -208,7 +215,7 @@ def eval_debug(self, expression): 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) + code = 'PyJsEvalResult = eval(%s)' % json.dumps(expression) self.execute_debug(code) return self['PyJsEvalResult'] @@ -240,21 +247,16 @@ def console(self): if DEBUG: sys.stderr.write(traceback.format_exc()) else: - sys.stderr.write('EXCEPTION: '+str(e)+'\n') + sys.stderr.write('EXCEPTION: ' + str(e) + '\n') time.sleep(0.01) - - #print x - - -if __name__=='__main__': +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() - diff --git a/js2py/host/console.py b/js2py/host/console.py index 822219ea..3b44a479 100644 --- a/js2py/host/console.py +++ b/js2py/host/console.py @@ -1,11 +1,14 @@ from ..base import * + @Js def console(): pass + @Js def log(): print(arguments[0]) -console.put('log', log) \ No newline at end of file + +console.put('log', log) diff --git a/js2py/host/jseval.py b/js2py/host/jseval.py index 53666720..13140d2d 100644 --- a/js2py/host/jseval.py +++ b/js2py/host/jseval.py @@ -5,6 +5,7 @@ except: pass + @Js def Eval(code): local_scope = inspect.stack()[3][0].f_locals['var'] @@ -20,22 +21,24 @@ def Eval(code): # a simple way to return value from eval. Will not work in complex cases. has_return = False for n in xrange(len(lines)): - line = lines[len(lines)-n-1] + line = lines[len(lines) - n - 1] if line.strip(): if line.startswith(' '): break - elif line.strip()=='pass': + elif line.strip() == 'pass': continue - elif any(line.startswith(e) for e in ['return ', 'continue ', 'break', 'raise ']): + elif any( + line.startswith(e) + for e in ['return ', 'continue ', 'break', 'raise ']): break else: has_return = True - cand = 'EVAL_RESULT = (%s)\n'%line + cand = 'EVAL_RESULT = (%s)\n' % line try: compile(cand, '', 'exec') except SyntaxError: break - lines[len(lines)-n-1] = cand + lines[len(lines) - n - 1] = cand py_code = '\n'.join(lines) break #print py_code @@ -44,7 +47,5 @@ def Eval(code): return globals()['EVAL_RESULT'] - def executor(code): - exec(code, globals()) - + exec (code, globals()) diff --git a/js2py/host/jsfunctions.py b/js2py/host/jsfunctions.py index f7cd815f..2360fee8 100644 --- a/js2py/host/jsfunctions.py +++ b/js2py/host/jsfunctions.py @@ -1,71 +1,132 @@ from ..base import * from six.moves.urllib.parse import quote, unquote +RADIX_CHARS = { + '1': 1, + '0': 0, + '3': 3, + '2': 2, + '5': 5, + '4': 4, + '7': 7, + '6': 6, + '9': 9, + '8': 8, + 'a': 10, + 'c': 12, + 'b': 11, + 'e': 14, + 'd': 13, + 'g': 16, + 'f': 15, + 'i': 18, + 'h': 17, + 'k': 20, + 'j': 19, + 'm': 22, + 'l': 21, + 'o': 24, + 'n': 23, + 'q': 26, + 'p': 25, + 's': 28, + 'r': 27, + 'u': 30, + 't': 29, + 'w': 32, + 'v': 31, + 'y': 34, + 'x': 33, + 'z': 35, + 'A': 10, + 'C': 12, + 'B': 11, + 'E': 14, + 'D': 13, + 'G': 16, + 'F': 15, + 'I': 18, + 'H': 17, + 'K': 20, + 'J': 19, + 'M': 22, + 'L': 21, + 'O': 24, + 'N': 23, + 'Q': 26, + 'P': 25, + 'S': 28, + 'R': 27, + 'U': 30, + 'T': 29, + 'W': 32, + 'V': 31, + 'Y': 34, + 'X': 33, + 'Z': 35 +} + -RADIX_CHARS = {'1': 1, '0': 0, '3': 3, '2': 2, '5': 5, '4': 4, '7': 7, '6': 6, '9': 9, '8': 8, 'a': 10, 'c': 12, - 'b': 11, 'e': 14, 'd': 13, 'g': 16, 'f': 15, 'i': 18, 'h': 17, 'k': 20, 'j': 19, 'm': 22, 'l': 21, - 'o': 24, 'n': 23, 'q': 26, 'p': 25, 's': 28, 'r': 27, 'u': 30, 't': 29, 'w': 32, 'v': 31, 'y': 34, - 'x': 33, 'z': 35, 'A': 10, 'C': 12, 'B': 11, 'E': 14, 'D': 13, 'G': 16, 'F': 15, 'I': 18, 'H': 17, - 'K': 20, 'J': 19, 'M': 22, 'L': 21, 'O': 24, 'N': 23, 'Q': 26, 'P': 25, 'S': 28, 'R': 27, 'U': 30, - 'T': 29, 'W': 32, 'V': 31, 'Y': 34, 'X': 33, 'Z': 35} @Js -def parseInt (string , radix): +def parseInt(string, radix): string = string.to_string().value.lstrip() sign = 1 if string and string[0] in ('+', '-'): - if string[0]=='-': + if string[0] == '-': sign = -1 string = string[1:] r = radix.to_int32() strip_prefix = True if r: - if r<2 or r>36: + if r < 2 or r > 36: return NaN - if r!=16: + if r != 16: strip_prefix = False else: r = 10 if strip_prefix: - if len(string)>=2 and string[:2] in ('0x', '0X'): + if len(string) >= 2 and string[:2] in ('0x', '0X'): string = string[2:] r = 16 n = 0 - num = 0 - while n4: # cant be a number anymore + if failed > 4: # cant be a number anymore break length += 1 if num is None: return NaN - return sign*float(string[:max_len]) + return sign * float(string[:max_len]) + @Js def isNaN(number): @@ -73,6 +134,7 @@ def isNaN(number): return true return false + @Js def isFinite(number): num = number.to_number() @@ -83,26 +145,32 @@ def isFinite(number): # todo test them properly + @Js def escape(text): return quote(text.to_string().value) + @Js def unescape(text): return unquote(text.to_string().value) + @Js def encodeURI(text): return quote(text.to_string().value, safe='~@#$&()*!+=:;,.?/\'') + @Js def decodeURI(text): return unquote(text.to_string().value) + @Js def encodeURIComponent(text): return quote(text.to_string().value, safe='~()*!.\'') + @Js def decodeURIComponent(text): return unquote(text.to_string().value) diff --git a/js2py/internals/base.py b/js2py/internals/base.py index 9b33c6f1..ec277c64 100644 --- a/js2py/internals/base.py +++ b/js2py/internals/base.py @@ -10,12 +10,10 @@ from pyjsparser import PyJsParser from itertools import izip - from conversions import * from simplex import * - def Type(obj): return obj.TYPE @@ -39,7 +37,7 @@ def put_member(self, unconverted_prop, val): return self.put(to_string(unconverted_prop), val) def get(self, prop): - assert type(prop)==unicode + assert type(prop) == unicode cand = self.get_property(prop) if cand is None: return undefined @@ -76,12 +74,15 @@ def put(self, prop, val, throw=False): return desc = self.get_property(prop) if is_accessor_descriptor(desc): - desc['set'].call(self, (val,)) # calling setter on own or inherited element + desc['set'].call( + self, (val, )) # calling setter on own or inherited element else: # new property - self.own[prop] = {'value': val, - 'writable': True, - 'configurable': True, - 'enumerable': True} + self.own[prop] = { + 'value': val, + 'writable': True, + 'configurable': True, + 'enumerable': True + } def can_put(self, prop): # to check assert type(prop) == unicode, type(prop) @@ -89,7 +90,8 @@ def can_put(self, prop): # to check desc = self.get_own_property(prop) if desc: # if we have this property if is_accessor_descriptor(desc): - return is_callable(desc['set']) # Check if setter method is defined + return is_callable( + desc['set']) # Check if setter method is defined else: # data desc return desc['writable'] if self.prototype is None: @@ -131,9 +133,12 @@ def default_value(self, hint=None): cand = method.call(self, ()) if is_primitive(cand): return cand - raise MakeError('TypeError', 'Cannot convert object to primitive value') + raise MakeError('TypeError', + 'Cannot convert object to primitive value') - def define_own_property(self, prop, desc, throw): # Internal use only. External through Object + def define_own_property( + self, prop, desc, + throw): # Internal use only. External through Object assert type(prop) == unicode # takes Py, returns Py # prop must be a Py string. Desc is either a descriptor or accessor. @@ -144,21 +149,26 @@ def define_own_property(self, prop, desc, throw): # Internal use only. External if not current: # We are creating a new OWN property if not extensible: if throw: - raise MakeError('TypeError', 'Could not define own property') + raise MakeError('TypeError', + 'Could not define own property') return False # extensible must be True if is_data_descriptor(desc) or is_generic_descriptor(desc): - DEFAULT_DATA_DESC = {'value': undefined, # undefined - 'writable': False, - 'enumerable': False, - 'configurable': False} + DEFAULT_DATA_DESC = { + 'value': undefined, # undefined + 'writable': False, + 'enumerable': False, + 'configurable': False + } DEFAULT_DATA_DESC.update(desc) self.own[prop] = DEFAULT_DATA_DESC else: - DEFAULT_ACCESSOR_DESC = {'get': undefined, # undefined - 'set': undefined, # undefined - 'enumerable': False, - 'configurable': False} + DEFAULT_ACCESSOR_DESC = { + 'get': undefined, # undefined + 'set': undefined, # undefined + 'enumerable': False, + 'configurable': False + } DEFAULT_ACCESSOR_DESC.update(desc) self.own[prop] = DEFAULT_ACCESSOR_DESC return True @@ -169,11 +179,14 @@ def define_own_property(self, prop, desc, throw): # Internal use only. External if not configurable: # Prevent changing params if desc.get('configurable'): if throw: - raise MakeError('TypeError', 'Could not define own property') + raise MakeError('TypeError', + 'Could not define own property') return False - if 'enumerable' in desc and desc['enumerable'] != current['enumerable']: + if 'enumerable' in desc and desc['enumerable'] != current[ + 'enumerable']: if throw: - raise MakeError('TypeError', 'Could not define own property') + raise MakeError('TypeError', + 'Could not define own property') return False if is_generic_descriptor(desc): pass @@ -181,7 +194,8 @@ def define_own_property(self, prop, desc, throw): # Internal use only. External # we want to change the current type of property if not configurable: if throw: - raise MakeError('TypeError', 'Could not define own property') + raise MakeError('TypeError', + 'Could not define own property') return False if is_data_descriptor(current): # from data to setter del current['value'] @@ -197,21 +211,26 @@ def define_own_property(self, prop, desc, throw): # Internal use only. External if not configurable: if not current['writable'] and desc.get('writable'): if throw: - raise MakeError('TypeError', 'Could not define own property') + raise MakeError('TypeError', + 'Could not define own property') return False - if not current['writable'] and 'value' in desc and current['value'] != desc['value']: + if not current['writable'] and 'value' in desc and current[ + 'value'] != desc['value']: if throw: - raise MakeError('TypeError', 'Could not define own property') + raise MakeError('TypeError', + 'Could not define own property') return False elif is_accessor_descriptor(current) and is_accessor_descriptor(desc): if not configurable: if 'set' in desc and desc['set'] != current['set']: if throw: - raise MakeError('TypeError', 'Could not define own property') + raise MakeError('TypeError', + 'Could not define own property') return False if 'get' in desc and desc['get'] != current['get']: if throw: - raise MakeError('TypeError', 'Could not define own property') + raise MakeError('TypeError', + 'Could not define own property') return False current.update(desc) return True @@ -221,14 +240,18 @@ def create(self, args, space): raise MakeError('TypeError', '%s is not a constructor' % self.Class) -def get_member(self, prop, space): # general member getter, prop has to be unconverted prop. it is it can be any value +def get_member( + self, prop, space +): # general member getter, prop has to be unconverted prop. it is it can be any value typ = type(self) if typ not in PRIMITIVES: # most likely getter for object - return self.get_member(prop) # <- object can implement this to support faster prop getting. ex array. + return self.get_member( + prop + ) # <- object can implement this to support faster prop getting. ex array. elif typ == unicode: # then probably a String - if type(prop)==float and is_finite(prop): + if type(prop) == float and is_finite(prop): index = int(prop) - if index==prop and 0 <= index < len(self): + if index == prop and 0 <= index < len(self): return self[index] s_prop = to_string(prop) if s_prop == 'length': @@ -246,9 +269,11 @@ def get_member(self, prop, space): # general member getter, prop has to be uncon elif typ == bool: return space.BooleanPrototype.get(to_string(prop)) elif typ is UNDEFINED_TYPE: - raise MakeError('TypeError', "Cannot read property '%s' of undefined" % prop) + raise MakeError('TypeError', + "Cannot read property '%s' of undefined" % prop) elif typ is NULL_TYPE: - raise MakeError('TypeError', "Cannot read property '%s' of null" % prop) + raise MakeError('TypeError', + "Cannot read property '%s' of null" % prop) else: raise RuntimeError('Unknown type! - ' + repr(typ)) @@ -275,16 +300,19 @@ def get_member_dot(self, prop, space): elif typ == bool: return space.BooleanPrototype.get(prop) elif typ in (UNDEFINED_TYPE, NULL_TYPE): - raise MakeError('TypeError', "Cannot read property '%s' of undefined" % prop) + raise MakeError('TypeError', + "Cannot read property '%s' of undefined" % prop) else: raise RuntimeError('Unknown type! - ' + repr(typ)) # Object + class PyJsObject(PyJs): TYPE = 'Object' Class = 'Object' + def __init__(self, prototype=None): self.prototype = prototype self.own = {} @@ -292,22 +320,45 @@ def __init__(self, prototype=None): def _init(self, props, vals): i = 0 for prop, kind in props: - if prop in self.own: # just check... probably will not happen very often. + if prop in self.own: # just check... probably will not happen very often. if is_data_descriptor(self.own[prop]): - if kind!='i': - raise MakeError('SyntaxError', 'Invalid object initializer! Duplicate property name "%s"' % prop) + if kind != 'i': + raise MakeError( + 'SyntaxError', + 'Invalid object initializer! Duplicate property name "%s"' + % prop) else: - if kind=='i' or (kind=='g' and 'get' in self.own[prop]) or (kind=='s' and 'set' in self.own[prop]): - raise MakeError('SyntaxError', 'Invalid object initializer! Duplicate setter/getter of prop: "%s"' % prop) - - if kind == 'i': # init - self.own[prop] = {'value': vals[i], 'writable': True, 'enumerable': True, 'configurable': True} - elif kind == 'g': # get - self.define_own_property(prop, {'get': vals[i], 'enumerable': True, 'configurable': True}, False) - elif kind == 's': # get - self.define_own_property(prop, {'get': vals[i], 'enumerable': True, 'configurable': True}, False) + if kind == 'i' or (kind == 'g' and 'get' in self.own[prop] + ) or (kind == 's' + and 'set' in self.own[prop]): + raise MakeError( + 'SyntaxError', + 'Invalid object initializer! Duplicate setter/getter of prop: "%s"' + % prop) + + if kind == 'i': # init + self.own[prop] = { + 'value': vals[i], + 'writable': True, + 'enumerable': True, + 'configurable': True + } + elif kind == 'g': # get + self.define_own_property(prop, { + 'get': vals[i], + 'enumerable': True, + 'configurable': True + }, False) + elif kind == 's': # get + self.define_own_property(prop, { + 'get': vals[i], + 'enumerable': True, + 'configurable': True + }, False) else: - raise RuntimeError('Invalid property kind - %s. Expected one of i, g, s.' % repr(kind)) + raise RuntimeError( + 'Invalid property kind - %s. Expected one of i, g, s.' % + repr(kind)) i += 1 def _set_props(self, prop_descs): @@ -315,26 +366,36 @@ def _set_props(self, prop_descs): self.define_own_property(prop, desc) - - # Array + # todo Optimise Array - extremely slow due to index conversions from str to int and back etc. # solution - make get and put methods callable with any type of prop and handle conversions from inside # if not array then use to_string(prop). In array if prop is integer then just use it # also consider removal of these stupid writable, enumerable etc for ints. class PyJsArray(PyJs): Class = 'Array' + def __init__(self, length, prototype=None): self.prototype = prototype - self.own = {'length' : {'value': float(length), 'writable': True, - 'enumerable': False, 'configurable': False}} + self.own = { + 'length': { + 'value': float(length), + 'writable': True, + 'enumerable': False, + 'configurable': False + } + } def _init(self, elements): for i, ele in enumerate(elements): if ele is None: continue - self.own[unicode(i)] = {'value': ele, 'writable': True, - 'enumerable': True, 'configurable': True} + self.own[unicode(i)] = { + 'value': ele, + 'writable': True, + 'enumerable': True, + 'configurable': True + } def put(self, prop, val, throw=False): assert type(val) != int @@ -349,57 +410,66 @@ def put(self, prop, val, throw=False): return desc = self.get_property(prop) if is_accessor_descriptor(desc): - desc['set'].call(self, (val,)) # calling setter on own or inherited element + desc['set'].call( + self, (val, )) # calling setter on own or inherited element else: # new property - self.define_own_property(prop, {'value': val, - 'writable': True, - 'configurable': True, - 'enumerable': True}, False) - + self.define_own_property( + prop, { + 'value': val, + 'writable': True, + 'configurable': True, + 'enumerable': True + }, False) def define_own_property(self, prop, desc, throw): assert type(desc.get('value')) != int old_len_desc = self.get_own_property('length') old_len = old_len_desc['value'] # value is js type so convert to py. - if prop=='length': + if prop == 'length': if 'value' not in desc: return PyJs.define_own_property(self, prop, desc, False) - new_len = to_uint32(desc['value']) - if new_len!=to_number(desc['value']): + new_len = to_uint32(desc['value']) + if new_len != to_number(desc['value']): raise MakeError('RangeError', 'Invalid range!') - new_desc = dict((k,v) for k,v in six.iteritems(desc)) + new_desc = dict((k, v) for k, v in six.iteritems(desc)) new_desc['value'] = float(new_len) - if new_len>=old_len: + if new_len >= old_len: return PyJs.define_own_property(self, prop, new_desc, False) if not old_len_desc['writable']: return False - if 'writable' not in new_desc or new_desc['writable']==True: + if 'writable' not in new_desc or new_desc['writable'] == True: new_writable = True else: new_writable = False new_desc['writable'] = True if not PyJs.define_own_property(self, prop, new_desc, False): return False - if new_len30*len(self.own): + if old_len > 30 * len(self.own): for ele in self.own.keys(): - if ele.isdigit() and int(ele)>=new_len: - if not self.delete(ele): # if failed to delete set len to current len and reject. - new_desc['value'] = old_len+1. + if ele.isdigit() and int(ele) >= new_len: + if not self.delete( + ele + ): # if failed to delete set len to current len and reject. + new_desc['value'] = old_len + 1. if not new_writable: new_desc['writable'] = False - PyJs.define_own_property(self, prop, new_desc, False) + PyJs.define_own_property( + self, prop, new_desc, False) return False old_len = new_len - else: # standard method - while new_len=old_len and not old_len_desc['writable']: + if index >= old_len and not old_len_desc['writable']: return False if not PyJs.define_own_property(self, prop, desc, False): return False - if index>=old_len: + if index >= old_len: old_len_desc['value'] = index + 1. return True else: return PyJs.define_own_property(self, prop, desc, False) def to_list(self): - return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())] + return [ + self.get(str(e)) for e in xrange(self.get('length').to_uint32()) + ] # database with compiled patterns. Js pattern -> Py pattern. REGEXP_DB = {} + class PyJsRegExp(PyJs): Class = 'RegExp' @@ -440,34 +513,62 @@ def __init__(self, body, flags, prototype=None): comp = None try: # converting JS regexp pattern to Py pattern. - possible_fixes = [ - (u'[]', u'[\0]'), - (u'[^]', u'[^\0]'), - (u'nofix1791', u'nofix1791') - ] + possible_fixes = [(u'[]', u'[\0]'), (u'[^]', u'[^\0]'), + (u'nofix1791', u'nofix1791')] reg = self.value for fix, rep in possible_fixes: comp = PyJsParser()._interpret_regexp(reg, flags) #print 'reg -> comp', reg, '->', comp try: - self.pat = re.compile(comp, self.ignore_case | self.multiline) + self.pat = re.compile( + comp, self.ignore_case | self.multiline) #print reg, '->', comp break except: reg = reg.replace(fix, rep) - # print 'Fix', fix, '->', rep, '=', reg + # print 'Fix', fix, '->', rep, '=', reg else: raise Exception() REGEXP_DB[body, flags] = self.pat except: #print 'Invalid pattern...', self.value, comp - raise MakeError('SyntaxError', 'Invalid RegExp pattern: %s -> %s'% (repr(self.value), repr(comp))) + raise MakeError( + 'SyntaxError', + 'Invalid RegExp pattern: %s -> %s' % (repr(self.value), + repr(comp))) # now set own properties: - self.own = {'source' : {'value': self.value, 'enumerable': False, 'writable': False, 'configurable': False}, - 'global' : {'value': self.glob, 'enumerable': False, 'writable': False, 'configurable': False}, - 'ignoreCase' : {'value': bool(self.ignore_case), 'enumerable': False, 'writable': False, 'configurable': False}, - 'multiline' : {'value': bool(self.multiline), 'enumerable': False, 'writable': False, 'configurable': False}, - 'lastIndex' : {'value': 0., 'enumerable': False, 'writable': True, 'configurable': False}} + self.own = { + 'source': { + 'value': self.value, + 'enumerable': False, + 'writable': False, + 'configurable': False + }, + 'global': { + 'value': self.glob, + 'enumerable': False, + 'writable': False, + 'configurable': False + }, + 'ignoreCase': { + 'value': bool(self.ignore_case), + 'enumerable': False, + 'writable': False, + 'configurable': False + }, + 'multiline': { + 'value': bool(self.multiline), + 'enumerable': False, + 'writable': False, + 'configurable': False + }, + 'lastIndex': { + 'value': 0., + 'enumerable': False, + 'writable': True, + 'configurable': False + } + } def match(self, string, pos): '''string is of course a py string''' @@ -477,6 +578,7 @@ def match(self, string, pos): class PyJsError(PyJs): Class = 'Error' extensible = True + def __init__(self, message=None, prototype=None): self.prototype = prototype self.own = {} @@ -485,10 +587,9 @@ def __init__(self, message=None, prototype=None): self.own['message']['enumerable'] = False - class PyJsDate(PyJs): Class = 'Date' - UTCToLocal = None # todo UTC to local should be imported! + UTCToLocal = None # todo UTC to local should be imported! def __init__(self, value, prototype=None): self.value = value @@ -497,10 +598,11 @@ def __init__(self, value, prototype=None): # todo fix this problematic datetime part def to_local_dt(self): - return datetime.datetime.utcfromtimestamp(self.UTCToLocal(self.value)//1000) + return datetime.datetime.utcfromtimestamp( + self.UTCToLocal(self.value) // 1000) def to_utc_dt(self): - return datetime.datetime.utcfromtimestamp(self.value//1000) + return datetime.datetime.utcfromtimestamp(self.value // 1000) def local_strftime(self, pattern): if self.value is NaN: @@ -508,11 +610,16 @@ def local_strftime(self, pattern): try: dt = self.to_local_dt() except: - raise MakeError('TypeError', 'unsupported date range. Will fix in future versions') + 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)') + 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: @@ -520,11 +627,17 @@ def utc_strftime(self, pattern): try: dt = self.to_utc_dt() except: - raise MakeError('TypeError', 'unsupported date range. Will fix in future versions') + 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)') + raise MakeError( + 'TypeError', + 'Could not generate date string from this date (limitations of python.datetime)' + ) + # Scope class it will hold all the variables accessible to user class Scope(PyJs): @@ -555,8 +668,13 @@ def __init__(self, scope, space, parent=None): self.own = {} for k, v in six.iteritems(scope): # set all the global items - self.define_own_property(k, {'value': v, 'configurable': False, - 'writable': False, 'enumerable': False}, False) + self.define_own_property( + k, { + 'value': v, + 'configurable': False, + 'writable': False, + 'enumerable': False + }, False) else: # not global, less powerful but faster closure. self.own = scope # simple dictionary which maps name directly to js object. @@ -571,8 +689,13 @@ def register(self, var): if var in self.own: self.own[var]['configurable'] = False else: - self.define_own_property(var, {'value': undefined, 'configurable': False, - 'writable': True, 'enumerable': True}, False) + self.define_own_property( + var, { + 'value': undefined, + 'configurable': False, + 'writable': True, + 'enumerable': True + }, False) elif var not in self.own: # define in local scope since it has not been defined yet self.own[var] = undefined # default value @@ -608,7 +731,8 @@ def put(self, var, val, throw=False): def get(self, var, throw=False): if self.prototype is not None: if self.is_with_scope: - cand = None if not self.own.has_property(var) else self.own.get(var) + cand = None if not self.own.has_property( + var) else self.own.get(var) else: # fast local scope cand = self.own.get(var) @@ -648,25 +772,40 @@ def delete(self, var, throw=False): def get_new_arguments_obj(args, space): obj = space.NewObject() obj.Class = 'Arguments' - obj.define_own_property('length', {'value': float(len(args)), 'writable': True, 'enumerable': False, 'configurable': True}, False) + obj.define_own_property( + 'length', { + 'value': float(len(args)), + 'writable': True, + 'enumerable': False, + 'configurable': True + }, False) for i, e in enumerate(args): obj.put(unicode(i), e) return obj - #Function class PyJsFunction(PyJs): Class = 'Function' source = '{ [native code] }' IS_CONSTRUCTOR = True - def __init__(self, code, ctx, params, name, space, is_declaration, definitions, prototype=None): + def __init__(self, + code, + ctx, + params, + name, + space, + is_declaration, + definitions, + prototype=None): self.prototype = prototype self.own = {} self.code = code - if type(self.code) == int: # just a label pointing to the beginning of the code. + if type( + self.code + ) == int: # just a label pointing to the beginning of the code. self.is_native = False else: self.is_native = True # python function @@ -686,27 +825,51 @@ def __init__(self, code, ctx, params, name, space, is_declaration, definitions, self.is_declaration = is_declaration #set own property length to the number of arguments - self.own['length'] = {'value': float(len(params)), 'writable': False, 'enumerable': False, 'configurable': False} + self.own['length'] = { + 'value': float(len(params)), + 'writable': False, + 'enumerable': False, + 'configurable': False + } if name: - self.own['name'] = {'value': name, 'writable': False, 'enumerable': False, 'configurable': True} + self.own['name'] = { + 'value': name, + 'writable': False, + 'enumerable': False, + 'configurable': True + } if not self.is_native: # set prototype for user defined functions # constructor points to this function proto = space.NewObject() - proto.own['constructor'] = {'value': self, 'writable': True, 'enumerable': False, 'configurable': True} - self.own['prototype'] = {'value': proto, 'writable': True, 'enumerable': False, 'configurable': False} + proto.own['constructor'] = { + 'value': self, + 'writable': True, + 'enumerable': False, + 'configurable': True + } + self.own['prototype'] = { + 'value': proto, + 'writable': True, + 'enumerable': False, + 'configurable': False + } # todo set up throwers on callee and arguments if in strict mode - def call(self, this, args=()): ''' Dont use this method from inside bytecode to call other bytecode. ''' if self.is_native: - _args = SpaceTuple(args) # we have to do that unfortunately to pass all the necessary info to the funcs + _args = SpaceTuple( + args + ) # we have to do that unfortunately to pass all the necessary info to the funcs _args.space = self.space - return self.code(this, _args) # must return valid js object - undefined, null, float, unicode, bool, or PyJs + return self.code( + this, _args + ) # must return valid js object - undefined, null, float, unicode, bool, or PyJs else: - return self.space.exe._call(self, this, args) # will run inside bytecode + return self.space.exe._call(self, this, + args) # will run inside bytecode def has_instance(self, other): # I am not sure here so instanceof may not work lol. @@ -714,7 +877,9 @@ def has_instance(self, other): return False proto = self.get('prototype') if not is_object(proto): - raise MakeError('TypeError', 'Function has non-object prototype in instanceof check') + raise MakeError( + 'TypeError', + 'Function has non-object prototype in instanceof check') while True: other = other.prototype if not other: # todo make sure that the condition is not None or null @@ -733,17 +898,19 @@ def create(self, args, space): return new def _generate_my_context(self, this, args): - my_ctx = Scope(dict(izip(self.params, args)), self.space, parent=self.ctx) + my_ctx = Scope( + dict(izip(self.params, args)), self.space, parent=self.ctx) my_ctx.registers(self.definitions) my_ctx.THIS_BINDING = this if not self.arguments_in_params: my_ctx.own['arguments'] = get_new_arguments_obj(args, self.space) if not self.is_declaration and self.name and self.name not in my_ctx.own: - my_ctx.own[self.name] = self # this should be immutable binding but come on! + my_ctx.own[ + self. + name] = self # this should be immutable binding but come on! return my_ctx - class SpaceTuple: def __init__(self, tup): self.tup = tup @@ -756,4 +923,3 @@ def __getitem__(self, item): def __iter__(self): return iter(self.tup) - diff --git a/js2py/internals/byte_trans.py b/js2py/internals/byte_trans.py index bfb0ec97..87fab4b4 100644 --- a/js2py/internals/byte_trans.py +++ b/js2py/internals/byte_trans.py @@ -23,22 +23,20 @@ def __init__(self, exe): self.states = [] - def record_state(self): - self.states.append((self.declared_continue_labels, self.declared_break_labels, - self.implicit_breaks, self.implicit_continues, - self.declared_vars, self.function_declaration_tape)) + self.states.append( + (self.declared_continue_labels, self.declared_break_labels, + self.implicit_breaks, self.implicit_continues, self.declared_vars, + self.function_declaration_tape)) self.declared_continue_labels, self.declared_break_labels, \ self.implicit_breaks, self.implicit_continues, \ self.declared_vars, self.function_declaration_tape = {}, {}, [], [], [], [] - def restore_state(self): self.declared_continue_labels, self.declared_break_labels, \ self.implicit_breaks, self.implicit_continues, \ self.declared_vars, self.function_declaration_tape = self.states.pop() - def ArrayExpression(self, elements, **kwargs): for e in elements: if e is None: @@ -46,7 +44,7 @@ def ArrayExpression(self, elements, **kwargs): else: self.emit(e) self.emit('LOAD_ARRAY', len(elements)) - + def AssignmentExpression(self, operator, left, right, **kwargs): operator = operator[:-1] if left['type'] == 'MemberExpression': @@ -61,36 +59,39 @@ def AssignmentExpression(self, operator, left, right, **kwargs): else: self.emit(right) if operator: - self.emit('STORE_MEMBER_DOT_OP', left['property']['name'], operator) + self.emit('STORE_MEMBER_DOT_OP', left['property']['name'], + operator) else: self.emit('STORE_MEMBER_DOT', left['property']['name']) elif left['type'] == 'Identifier': if left['name'] in SPECIAL_IDENTIFIERS: - raise MakeError('SyntaxError', 'Invalid left-hand side in assignment') + raise MakeError('SyntaxError', + 'Invalid left-hand side in assignment') self.emit(right) if operator: self.emit('STORE_OP', left['name'], operator) else: self.emit('STORE', left['name']) else: - raise MakeError('SyntaxError', 'Invalid left-hand side in assignment') + raise MakeError('SyntaxError', + 'Invalid left-hand side in assignment') - def BinaryExpression(self, operator, left, right, **kwargs): self.emit(left) self.emit(right) self.emit('BINARY_OP', operator) - + def BlockStatement(self, body, **kwargs): self._emit_statement_list(body) - + def BreakStatement(self, label, **kwargs): if label is None: self.emit('JUMP', self.implicit_breaks[-1]) else: label = label.get('name') if label not in self.declared_break_labels: - raise MakeError('SyntaxError', 'Undefined label \'%s\'' % label) + raise MakeError('SyntaxError', + 'Undefined label \'%s\'' % label) else: self.emit('JUMP', self.declared_break_labels[label]) @@ -124,13 +125,13 @@ def CallExpression(self, callee, arguments, **kwargs): self.emit('CALL') else: self.emit('CALL_NO_ARGS') - + def ClassBody(self, body, **kwargs): raise NotImplementedError('Not available in ECMA 5.1') - + def ClassDeclaration(self, id, superClass, body, **kwargs): raise NotImplementedError('Not available in ECMA 5.1') - + def ClassExpression(self, id, superClass, body, **kwargs): raise NotImplementedError('Classes not available in ECMA 5.1') @@ -148,20 +149,21 @@ def ConditionalExpression(self, test, consequent, alternate, **kwargs): self.emit(alternate) # end of ?: statement self.emit('LABEL', end) - + def ContinueStatement(self, label, **kwargs): if label is None: self.emit('JUMP', self.implicit_continues[-1]) else: label = label.get('name') if label not in self.declared_continue_labels: - raise MakeError('SyntaxError', 'Undefined label \'%s\'' % label) + raise MakeError('SyntaxError', + 'Undefined label \'%s\'' % label) else: self.emit('JUMP', self.declared_continue_labels[label]) - + def DebuggerStatement(self, **kwargs): self.EmptyStatement(**kwargs) - + def DoWhileStatement(self, body, test, **kwargs): continue_label = self.exe.get_new_label() break_label = self.exe.get_new_label() @@ -181,9 +183,9 @@ def DoWhileStatement(self, body, test, **kwargs): self.implicit_continues.pop() self.implicit_breaks.pop() - self.emit('JUMP', continue_label) # loop back + self.emit('JUMP', continue_label) # loop back self.emit('LABEL', break_label) - + def EmptyStatement(self, **kwargs): # do nothing pass @@ -193,7 +195,7 @@ def ExpressionStatement(self, expression, **kwargs): # pop the previous value and execute expression self.emit('POP') self.emit(expression) - + def ForStatement(self, init, test, update, body, **kwargs): continue_label = self.exe.get_new_label() break_label = self.exe.get_new_label() @@ -224,10 +226,9 @@ def ForStatement(self, init, test, update, body, **kwargs): self.implicit_continues.pop() self.implicit_breaks.pop() - self.emit('JUMP', continue_label) # loop back + self.emit('JUMP', continue_label) # loop back self.emit('LABEL', break_label) - def ForInStatement(self, left, right, body, **kwargs): # prepare the needed labels body_start_label = self.exe.get_new_label() @@ -237,19 +238,24 @@ def ForInStatement(self, left, right, body, **kwargs): # prepare the name if left['type'] == 'VariableDeclaration': if len(left['declarations']) != 1: - raise MakeError('SyntaxError', ' Invalid left-hand side in for-in loop: Must have a single binding.') + raise MakeError( + 'SyntaxError', + ' Invalid left-hand side in for-in loop: Must have a single binding.' + ) self.emit(left) name = left['declarations'][0]['id']['name'] - elif left['type']=='Identifier': + elif left['type'] == 'Identifier': name = left['name'] else: - raise MakeError('SyntaxError', 'Invalid left-hand side in for-loop') + raise MakeError('SyntaxError', + 'Invalid left-hand side in for-loop') # prepare the iterable self.emit(right) # emit ForIn Opcode - self.emit('FOR_IN', name, body_start_label, continue_label, break_label) + self.emit('FOR_IN', name, body_start_label, continue_label, + break_label) # a special continue position self.emit('LABEL', continue_label) @@ -265,16 +271,18 @@ def ForInStatement(self, left, right, body, **kwargs): self.emit('NOP') self.emit('LABEL', break_label) self.emit('NOP') - + def FunctionDeclaration(self, id, params, defaults, body, **kwargs): if defaults: raise NotImplementedError('Defaults not available in ECMA 5.1') # compile function - self.record_state() # cleans translator state and appends it to the stack so that it can be later restored + self.record_state( + ) # cleans translator state and appends it to the stack so that it can be later restored function_start = self.exe.get_new_label() function_declarations = self.exe.get_new_label() - declarations_done = self.exe.get_new_label() # put jump to this place at the and of function tape! + declarations_done = self.exe.get_new_label( + ) # put jump to this place at the and of function tape! function_end = self.exe.get_new_label() # skip the function if encountered externally @@ -302,7 +310,9 @@ def FunctionDeclaration(self, id, params, defaults, body, **kwargs): name = id.get('name') assert name is not None self.declared_vars.append(name) - self.function_declaration_tape.append(LOAD_FUNCTION(function_start, tuple(p['name'] for p in params), name, True, tuple(declared_vars))) + self.function_declaration_tape.append( + LOAD_FUNCTION(function_start, tuple(p['name'] for p in params), + name, True, tuple(declared_vars))) self.function_declaration_tape.append(STORE(name)) self.function_declaration_tape.append(POP()) @@ -311,10 +321,12 @@ def FunctionExpression(self, id, params, defaults, body, **kwargs): raise NotImplementedError('Defaults not available in ECMA 5.1') # compile function - self.record_state() # cleans translator state and appends it to the stack so that it can be later restored + self.record_state( + ) # cleans translator state and appends it to the stack so that it can be later restored function_start = self.exe.get_new_label() function_declarations = self.exe.get_new_label() - declarations_done = self.exe.get_new_label() # put jump to this place at the and of function tape! + declarations_done = self.exe.get_new_label( + ) # put jump to this place at the and of function tape! function_end = self.exe.get_new_label() # skip the function if encountered externally @@ -340,20 +352,20 @@ def FunctionExpression(self, id, params, defaults, body, **kwargs): # create function object and append to stack name = id.get('name') if id else None - self.emit('LOAD_FUNCTION', function_start, tuple(p['name'] for p in params), name, False, tuple(declared_vars)) + self.emit('LOAD_FUNCTION', function_start, + tuple(p['name'] for p in params), name, False, + tuple(declared_vars)) - - def Identifier(self, name, **kwargs): - if name=='true': + if name == 'true': self.emit('LOAD_BOOLEAN', 1) - elif name=='false': + elif name == 'false': self.emit('LOAD_BOOLEAN', 0) - elif name=='undefined': + elif name == 'undefined': self.emit('LOAD_UNDEFINED') else: self.emit('LOAD', name) - + def IfStatement(self, test, consequent, alternate, **kwargs): alt = self.exe.get_new_label() end = self.exe.get_new_label() @@ -370,10 +382,10 @@ def IfStatement(self, test, consequent, alternate, **kwargs): # end of if statement self.emit('LABEL', end) - def LabeledStatement(self, label, body, **kwargs): label = label['name'] - if body['type'] in ('WhileStatement', 'DoWhileStatement', 'ForStatement', 'ForInStatement'): + if body['type'] in ('WhileStatement', 'DoWhileStatement', + 'ForStatement', 'ForInStatement'): # Continue label available... Simply take labels defined by the loop. # It is important that they request continue label first self.declared_continue_labels[label] = self.exe._label_count + 1 @@ -389,7 +401,6 @@ def LabeledStatement(self, label, body, **kwargs): self.emit('LABEL', lbl) del self.declared_break_labels[label] - def Literal(self, value, **kwargs): if value is None: self.emit('LOAD_NULL') @@ -431,7 +442,7 @@ def MemberExpression(self, computed, object, property, **kwargs): else: self.emit(object) self.emit('LOAD_MEMBER_DOT', property['name']) - + def NewExpression(self, callee, arguments, **kwargs): self.emit(callee) if arguments: @@ -443,39 +454,42 @@ def NewExpression(self, callee, arguments, **kwargs): else: self.emit('NEW_NO_ARGS') - def ObjectExpression(self, properties, **kwargs): data = [] for prop in properties: self.emit(prop['value']) if prop['computed']: - raise NotImplementedError('ECMA 5.1 does not support computed object properties!') + raise NotImplementedError( + 'ECMA 5.1 does not support computed object properties!') data.append((to_key(prop['key']), prop['kind'][0])) self.emit('LOAD_OBJECT', tuple(data)) - + def Program(self, body, **kwargs): self.emit('LOAD_UNDEFINED') self.emit(body) # add function tape ! self.exe.tape = self.function_declaration_tape + self.exe.tape - + def Pyimport(self, imp, **kwargs): - raise NotImplementedError('Not available for bytecode interpreter yet, use the Js2Py translator.') - - def Property(self, kind, key, computed, value, method, shorthand, **kwargs): + raise NotImplementedError( + 'Not available for bytecode interpreter yet, use the Js2Py translator.' + ) + + def Property(self, kind, key, computed, value, method, shorthand, + **kwargs): raise NotImplementedError('Not available in ECMA 5.1') - + def RestElement(self, argument, **kwargs): raise NotImplementedError('Not available in ECMA 5.1') - + def ReturnStatement(self, argument, **kwargs): - self.emit('POP') # pop result of expression statements + self.emit('POP') # pop result of expression statements if argument is None: self.emit('LOAD_UNDEFINED') else: self.emit(argument) self.emit('RETURN') - + def SequenceExpression(self, expressions, **kwargs): for e in expressions: self.emit(e) @@ -484,7 +498,7 @@ def SequenceExpression(self, expressions, **kwargs): def SwitchCase(self, test, consequent, **kwargs): raise NotImplementedError('Already implemented in SwitchStatement') - + def SwitchStatement(self, discriminant, cases, **kwargs): self.emit(discriminant) labels = [self.exe.get_new_label() for case in cases] @@ -513,16 +527,15 @@ def SwitchStatement(self, discriminant, cases, **kwargs): self.emit('LABEL', end_of_switch) - def ThisExpression(self, **kwargs): self.emit('LOAD_THIS') - + def ThrowStatement(self, argument, **kwargs): # throw with the empty stack self.emit('POP') self.emit(argument) self.emit('THROW') - + def TryStatement(self, block, handler, finalizer, **kwargs): try_label = self.exe.get_new_label() catch_label = self.exe.get_new_label() @@ -535,7 +548,9 @@ def TryStatement(self, block, handler, finalizer, **kwargs): self.emit('LABEL', try_label) self.emit('LOAD_UNDEFINED') self.emit(block) - self.emit('NOP') # needed to distinguish from break/continue vs some internal jumps + self.emit( + 'NOP' + ) # needed to distinguish from break/continue vs some internal jumps # catch block self.emit('LABEL', catch_label) @@ -554,17 +569,20 @@ def TryStatement(self, block, handler, finalizer, **kwargs): self.emit('LABEL', end_label) # give life to the code - self.emit('TRY_CATCH_FINALLY', try_label, catch_label, handler['param']['name'] if handler else None, finally_label, bool(finalizer), end_label) - + self.emit('TRY_CATCH_FINALLY', try_label, catch_label, + handler['param']['name'] if handler else None, finally_label, + bool(finalizer), end_label) def UnaryExpression(self, operator, argument, **kwargs): - if operator == 'typeof' and argument['type']=='Identifier': # todo fix typeof + if operator == 'typeof' and argument[ + 'type'] == 'Identifier': # todo fix typeof self.emit('TYPEOF', argument['name']) elif operator == 'delete': if argument['type'] == 'MemberExpression': self.emit(argument['object']) if argument['property']['type'] == 'Identifier': - self.emit('LOAD_STRING', unicode(argument['property']['name'])) + self.emit('LOAD_STRING', + unicode(argument['property']['name'])) else: self.emit(argument['property']) self.emit('DELETE_MEMBER') @@ -576,7 +594,8 @@ def UnaryExpression(self, operator, argument, **kwargs): self.emit(argument) self.emit('UNARY_OP', operator) else: - raise MakeError('SyntaxError', 'Unknown unary operator %s' % operator) + raise MakeError('SyntaxError', + 'Unknown unary operator %s' % operator) def UpdateExpression(self, operator, argument, prefix, **kwargs): incr = int(operator == "++") @@ -594,18 +613,19 @@ def UpdateExpression(self, operator, argument, prefix, **kwargs): name = to_key(argument) self.emit('POSTFIX', post, incr, name) else: - raise MakeError('SyntaxError', 'Invalid left-hand side in assignment') + raise MakeError('SyntaxError', + 'Invalid left-hand side in assignment') - def VariableDeclaration(self, declarations, kind, **kwargs): if kind != 'var': - raise NotImplementedError('Only var variable declaration is supported by ECMA 5.1') + raise NotImplementedError( + 'Only var variable declaration is supported by ECMA 5.1') for d in declarations: self.emit(d) def LexicalDeclaration(self, declarations, kind, **kwargs): raise NotImplementedError('Not supported by ECMA 5.1') - + def VariableDeclarator(self, id, init, **kwargs): name = id['name'] if name in SPECIAL_IDENTIFIERS: @@ -632,11 +652,9 @@ def WhileStatement(self, test, body, **kwargs): self.implicit_continues.pop() self.implicit_breaks.pop() - self.emit('JUMP', continue_label) # loop back + self.emit('JUMP', continue_label) # loop back self.emit('LABEL', break_label) - - def WithStatement(self, object, body, **kwargs): beg_label = self.exe.get_new_label() end_label = self.exe.get_new_label() @@ -654,8 +672,6 @@ def WithStatement(self, object, body, **kwargs): # with statement implementation self.emit('WITH', beg_label, end_label) - - def _emit_statement_list(self, statements): for statement in statements: self.emit(statement) @@ -669,8 +685,10 @@ def emit(self, what, *args): else: return getattr(self, what['type'])(**what) + import os, codecs + def path_as_local(path): if os.path.isabs(path): return path @@ -687,7 +705,6 @@ def get_file_contents(path_or_file): return js - def main(): from space import Space import fill_space @@ -702,7 +719,8 @@ def main(): a.exe.space = s s.exe = a.exe con = get_file_contents('internals/esprima.js') - d = parse(con+(''';JSON.stringify(exports.parse(%s), 4, 4)''' % json.dumps(con))) + d = parse(con + ( + ''';JSON.stringify(exports.parse(%s), 4, 4)''' % json.dumps(con))) # d = parse(''' # function x(n) { # log(n) @@ -721,18 +739,14 @@ def main(): print a.exe.tape print len(a.exe.tape) - a.exe.compile() def log(this, args): print args[0] return 999 - print a.exe.run(a.exe.space.GlobalObj) -if __name__=='__main__': +if __name__ == '__main__': main() - - diff --git a/js2py/internals/code.py b/js2py/internals/code.py index 77f3962d..6bd6739f 100644 --- a/js2py/internals/code.py +++ b/js2py/internals/code.py @@ -2,8 +2,10 @@ from space import * from base import * + class Code: '''Can generate, store and run sequence of ops representing js code''' + def __init__(self, is_strict=False): self.tape = [] self.compiled = False @@ -50,7 +52,7 @@ def _call(self, func, this, args): old_curr_ctx = self.current_ctx self.contexts = [FakeCtx()] - self.return_locs = [len(self.tape)] # target line after return + self.return_locs = [len(self.tape)] # target line after return # prepare my ctx my_ctx = func._generate_my_context(this, args) @@ -77,7 +79,8 @@ def execute_fragment_under_context(self, ctx, start_label, end_label): old_curr_ctx = self.current_ctx try: self.current_ctx = ctx - return self._execute_fragment_under_context(ctx, start_label, end_label) + return self._execute_fragment_under_context( + ctx, start_label, end_label) except JsException as err: # undo the things that were put on the stack (if any) # don't worry, I know the recovery is possible through try statement and for this reason try statement @@ -99,8 +102,9 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): #print loc, self.tape[loc] if len(self.contexts) == entry_level and loc >= end: assert loc == end - assert len(ctx.stack) == (1 + initial_len), 'Stack change must be equal to +1!' - return ctx.stack.pop(), 0, None # means normal return + assert len(ctx.stack) == ( + 1 + initial_len), 'Stack change must be equal to +1!' + return ctx.stack.pop(), 0, None # means normal return # execute instruction status = self.tape[loc].eval(ctx) @@ -112,16 +116,18 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): if len(self.contexts) == entry_level: # check if jumped outside of the fragment and break if so if not start <= loc < end: - assert len(ctx.stack) == (1+initial_len), 'Stack change must be equal to +1!' + assert len(ctx.stack) == ( + 1 + initial_len + ), 'Stack change must be equal to +1!' return ctx.stack.pop(), 2, status # jump outside continue - elif len(status)==2: # a call or a return! + elif len(status) == 2: # a call or a return! # call: (new_ctx, func_loc_label_num) if status[0] is not None: # append old state to the stack self.contexts.append(ctx) - self.return_locs.append(loc+1) + self.return_locs.append(loc + 1) # set new state loc = self.label_locs[status[1]] ctx = status[0] @@ -132,7 +138,8 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): else: if len(self.contexts) == entry_level: assert len(ctx.stack) == 1 + initial_len - return undefined, 1, ctx.stack.pop() # return signal + return undefined, 1, ctx.stack.pop( + ) # return signal return_value = ctx.stack.pop() ctx = self.contexts.pop() self.current_ctx = ctx @@ -158,12 +165,12 @@ def run(self, ctx, starting_loc=0): loc = self.label_locs[status] continue - elif len(status)==2: # a call or a return! + elif len(status) == 2: # a call or a return! # call: (new_ctx, func_loc_label_num) if status[0] is not None: # append old state to the stack self.contexts.append(ctx) - self.return_locs.append(loc+1) + self.return_locs.append(loc + 1) # set new state loc = self.label_locs[status[1]] ctx = status[0] @@ -184,6 +191,7 @@ def run(self, ctx, starting_loc=0): assert len(ctx.stack) == 1, ctx.stack return ctx.stack.pop() + class FakeCtx(object): def __init__(self): - self.stack = [] \ No newline at end of file + self.stack = [] diff --git a/js2py/internals/constructors/jsarray.py b/js2py/internals/constructors/jsarray.py index 83250fa9..23364ce4 100644 --- a/js2py/internals/constructors/jsarray.py +++ b/js2py/internals/constructors/jsarray.py @@ -2,19 +2,21 @@ from ..func_utils import * - def Array(this, args): return ArrayConstructor(args, args.space) def ArrayConstructor(args, space): - if len(args)==1: + if len(args) == 1: l = get_arg(args, 0) if type(l) == float: - if to_uint32(l)==l: + if to_uint32(l) == l: return space.NewArray(l) else: - raise MakeError('RangeError', 'Invalid length specified for Array constructor (must be uint32)') + raise MakeError( + 'RangeError', + 'Invalid length specified for Array constructor (must be uint32)' + ) else: return space.ConstructArray([l]) else: @@ -23,5 +25,4 @@ def ArrayConstructor(args, space): def isArray(this, args): x = get_arg(args, 0) - return is_object(x) and x.Class==u'Array' - + return is_object(x) and x.Class == u'Array' diff --git a/js2py/internals/constructors/jsboolean.py b/js2py/internals/constructors/jsboolean.py index 01c3f3b9..17025d9f 100644 --- a/js2py/internals/constructors/jsboolean.py +++ b/js2py/internals/constructors/jsboolean.py @@ -2,7 +2,6 @@ from ..func_utils import * - def Boolean(this, args): return to_boolean(get_arg(args, 0)) diff --git a/js2py/internals/constructors/jsdate.py b/js2py/internals/constructors/jsdate.py index d88fbd77..98de6431 100644 --- a/js2py/internals/constructors/jsdate.py +++ b/js2py/internals/constructors/jsdate.py @@ -1,60 +1,74 @@ from ..base import * from .time_helpers import * -TZ_OFFSET = (time.altzone//3600) +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) + return PyJsDate(int(time.time() * 1000), prototype=DatePrototype) @Js -def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this +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: + 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) + 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('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('parse', {'value': parse, - 'enumerable': False, - 'writable': True, - 'configurable': True}) +Date.define_own_property('UTC', { + 'value': UTC, + '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 = {} @@ -62,10 +76,11 @@ def __init__(self, value, prototype=None): # todo fix this problematic datetime part def to_local_dt(self): - return datetime.datetime.utcfromtimestamp(UTCToLocal(self.value)//1000) + return datetime.datetime.utcfromtimestamp( + UTCToLocal(self.value) // 1000) def to_utc_dt(self): - return datetime.datetime.utcfromtimestamp(self.value//1000) + return datetime.datetime.utcfromtimestamp(self.value // 1000) def local_strftime(self, pattern): if self.value is NaN: @@ -73,11 +88,16 @@ def local_strftime(self, pattern): try: dt = self.to_local_dt() except: - raise MakeError('TypeError', 'unsupported date range. Will fix in future versions') + 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)') + 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: @@ -85,31 +105,40 @@ def utc_strftime(self, pattern): try: dt = self.to_utc_dt() except: - raise MakeError('TypeError', 'unsupported date range. Will fix in future versions') + 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)') + 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 +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))) + 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) - + 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: + if len(args) >= 2: return date_constructor2(*args) - elif len(args)==1: + elif len(args) == 1: return date_constructor1(args[0]) else: return date_constructor0() @@ -121,7 +150,7 @@ def date_constructor0(): def date_constructor1(value): v = value.to_primitive() - if v._type()=='String': + if v._type() == 'String': v = parse_date(v.value) else: v = v.to_int() @@ -132,22 +161,25 @@ 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: + 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)))) + 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': + if obj.Class != 'Date': raise MakeError('TypeError', 'this is not a Date object') @@ -156,8 +188,10 @@ 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)) + 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) @@ -287,8 +321,7 @@ def getTimezoneOffset(): check_date(this) if this.value is NaN: return NaN - return (this.value - UTCToLocal(this.value))//60000 - + return (this.value - UTCToLocal(this.value)) // 60000 def setTime(time): check_date(this) @@ -298,7 +331,8 @@ def setTime(time): def setMilliseconds(ms): check_date(this) t = UTCToLocal(this.value) - tim = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) + tim = MakeTime( + HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) u = TimeClip(LocalToUTC(MakeDate(Day(t), tim))) this.value = u return u @@ -306,7 +340,8 @@ def setMilliseconds(ms): def setUTCMilliseconds(ms): check_date(this) t = this.value - tim = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) + tim = MakeTime( + HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) u = TimeClip(MakeDate(Day(t), tim)) this.value = u return u @@ -321,13 +356,17 @@ 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) + 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(): + if tv.Class == 'Number' and not tv.is_finite(): return this.null toISO = o.get('toISOString') if not toISO.is_callable(): @@ -338,34 +377,29 @@ def toJSON(key): def pad(num, n=2, sign=False): '''returns n digit string representation of the num''' s = unicode(abs(num)) - if len(s)=0: - return '+'+s + if num >= 0: + return '+' + s else: - return '-'+s - - - - - - - - + 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}) \ No newline at end of file +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 +}) diff --git a/js2py/internals/constructors/jsfunction.py b/js2py/internals/constructors/jsfunction.py index 29f81b20..9728fb38 100644 --- a/js2py/internals/constructors/jsfunction.py +++ b/js2py/internals/constructors/jsfunction.py @@ -5,8 +5,6 @@ from ..byte_trans import ByteCodeGenerator, Code - - def Function(this, args): # convert arguments to python list of strings a = map(to_string, tuple(args)) @@ -18,11 +16,11 @@ def Function(this, args): 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) + co = executable_code( + code_str=func_str, space=space, global_context=global_context) return co() @@ -44,20 +42,26 @@ def executable_code(code_str, space, global_context=True): 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! + 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) + 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: + elif status == 3: raise token else: - raise RuntimeError('Unexpected return status during JIT execution: %d' % status) + raise RuntimeError( + 'Unexpected return status during JIT execution: %d' % status) + return ex_code @@ -65,6 +69,7 @@ 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 \ No newline at end of file + return undefined diff --git a/js2py/internals/constructors/jsmath.py b/js2py/internals/constructors/jsmath.py index 467141ff..3eb44616 100644 --- a/js2py/internals/constructors/jsmath.py +++ b/js2py/internals/constructors/jsmath.py @@ -6,29 +6,30 @@ import math import random -CONSTANTS = {'E': 2.7182818284590452354, - 'LN10': 2.302585092994046, - 'LN2': 0.6931471805599453, - 'LOG2E': 1.4426950408889634, - 'LOG10E': 0.4342944819032518, - 'PI': 3.1415926535897932, - 'SQRT1_2': 0.7071067811865476, - 'SQRT2': 1.4142135623730951} - +CONSTANTS = { + 'E': 2.7182818284590452354, + 'LN10': 2.302585092994046, + 'LN2': 0.6931471805599453, + 'LOG2E': 1.4426950408889634, + 'LOG10E': 0.4342944819032518, + 'PI': 3.1415926535897932, + 'SQRT1_2': 0.7071067811865476, + 'SQRT2': 1.4142135623730951 +} class MathFunctions: def abs(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN return abs(a) def acos(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN try: return math.acos(a) @@ -38,7 +39,7 @@ def acos(this, args): def asin(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN try: return math.asin(a) @@ -48,7 +49,7 @@ def asin(this, args): def atan(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN return math.atan(a) @@ -57,56 +58,56 @@ def atan2(this, args): y = get_arg(args, 1) a = to_number(x) b = to_number(y) - if a!=a or b!=b: # it must be a nan + if a != a or b != b: # it must be a nan return NaN return math.atan2(a, b) def ceil(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN return float(math.ceil(a)) def floor(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN return float(math.floor(a)) def round(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN return float(round(a)) def sin(this, args): x = get_arg(args, 0) a = to_number(x) - if not is_finite(a): # it must be a nan + if not is_finite(a): # it must be a nan return NaN return math.sin(a) def cos(this, args): x = get_arg(args, 0) a = to_number(x) - if not is_finite(a): # it must be a nan + if not is_finite(a): # it must be a nan return NaN return math.cos(a) def tan(this, args): x = get_arg(args, 0) a = to_number(x) - if not is_finite(a): # it must be a nan + if not is_finite(a): # it must be a nan return NaN return math.tan(a) def log(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN try: return math.log(a) @@ -116,7 +117,7 @@ def log(this, args): def exp(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN return math.exp(a) @@ -125,7 +126,7 @@ def pow(this, args): y = get_arg(args, 1) a = to_number(x) b = to_number(y) - if a!=a or b!=b: # it must be a nan + if a != a or b != b: # it must be a nan return NaN try: return a**b @@ -135,7 +136,7 @@ def pow(this, args): def sqrt(this, args): x = get_arg(args, 0) a = to_number(x) - if a!=a: # it must be a nan + if a != a: # it must be a nan return NaN try: return a**0.5 @@ -143,15 +144,14 @@ def sqrt(this, args): return NaN def min(this, args): - if len(args)==0: + if len(args) == 0: return Infinity return min(map(to_number, tuple(args))) def max(this, args): - if len(args)==0: + if len(args) == 0: return -Infinity return max(map(to_number, tuple(args))) - def random(this, args): return random.random() diff --git a/js2py/internals/constructors/jsnumber.py b/js2py/internals/constructors/jsnumber.py index fa51d64e..276b7cfa 100644 --- a/js2py/internals/constructors/jsnumber.py +++ b/js2py/internals/constructors/jsnumber.py @@ -4,7 +4,6 @@ from ..func_utils import * - def Number(this, args): if len(args) == 0: return 0. @@ -15,16 +14,14 @@ def NumberConstructor(args, space): temp = space.NewObject() temp.prototype = space.NumberPrototype temp.Class = 'Number' - temp.value = float(to_number(get_arg(args, 0)) if len(args)>0 else 0.) + temp.value = float(to_number(get_arg(args, 0)) if len(args) > 0 else 0.) return temp CONSTS = { - 'MAX_VALUE':1.7976931348623157e308, - 'MIN_VALUE': 5.0e-324, - 'NaN': NaN, - 'NEGATIVE_INFINITY': Infinity, - 'POSITIVE_INFINITY': -Infinity} - - - + 'MAX_VALUE': 1.7976931348623157e308, + 'MIN_VALUE': 5.0e-324, + 'NaN': NaN, + 'NEGATIVE_INFINITY': Infinity, + 'POSITIVE_INFINITY': -Infinity +} diff --git a/js2py/internals/constructors/jsobject.py b/js2py/internals/constructors/jsobject.py index c683e57c..06fdbbb0 100644 --- a/js2py/internals/constructors/jsobject.py +++ b/js2py/internals/constructors/jsobject.py @@ -23,41 +23,45 @@ def ObjectCreate(args, space): return space.NewObject() - class ObjectMethods: def getPrototypeOf(this, args): obj = get_arg(args, 0) if not is_object(obj): - raise MakeError('TypeError', 'Object.getPrototypeOf called on non-object') + raise MakeError('TypeError', + 'Object.getPrototypeOf called on non-object') return null if obj.prototype is None else obj.prototype def getOwnPropertyDescriptor(this, args): obj = get_arg(args, 0) prop = get_arg(args, 1) if not is_object(obj): - raise MakeError('TypeError', 'Object.getOwnPropertyDescriptor called on non-object') + raise MakeError( + 'TypeError', + 'Object.getOwnPropertyDescriptor called on non-object') desc = obj.own.get(to_string(prop)) return convert_to_js_type(desc, args.space) - def getOwnPropertyNames(this, args): obj = get_arg(args, 0) if not is_object(obj): - raise MakeError('TypeError', 'Object.getOwnPropertyDescriptor called on non-object') + raise MakeError( + 'TypeError', + 'Object.getOwnPropertyDescriptor called on non-object') return args.space.ConstructArray(obj.own.keys()) def create(this, args): obj = get_arg(args, 0) if not (is_object(obj) or is_null(obj)): - raise MakeError('TypeError', 'Object prototype may only be an Object or null') + raise MakeError('TypeError', + 'Object prototype may only be an Object or null') temp = args.space.NewObject() temp.prototype = None if is_null(obj) else obj - if len(args)>1 and not is_undefined(args[1]): + if len(args) > 1 and not is_undefined(args[1]): if six.PY2: - args.tup = (args[1],) + args.tup = (args[1], ) ObjectMethods.defineProperties.__func__(temp, args) else: - args.tup = (args[1],) + args.tup = (args[1], ) ObjectMethods.defineProperties(temp, args) return temp @@ -66,9 +70,11 @@ def defineProperty(this, args): prop = get_arg(args, 1) attrs = get_arg(args, 2) if not is_object(obj): - raise MakeError('TypeError', 'Object.defineProperty called on non-object') + raise MakeError('TypeError', + 'Object.defineProperty called on non-object') name = to_string(prop) - if not obj.define_own_property(name, ToPropertyDescriptor(attrs), False): + if not obj.define_own_property(name, ToPropertyDescriptor(attrs), + False): raise MakeError('TypeError', 'Cannot redefine property: %s' % name) return obj @@ -76,14 +82,16 @@ def defineProperties(this, args): obj = get_arg(args, 0) properties = get_arg(args, 1) if not is_object(obj): - raise MakeError('TypeError', 'Object.defineProperties called on non-object') + raise MakeError('TypeError', + 'Object.defineProperties called on non-object') props = to_object(properties, args.space) for k, v in props.own.items(): if not v.get('enumerable'): continue desc = ToPropertyDescriptor(props.get(unicode(k))) if not obj.define_own_property(unicode(k), desc, False): - raise MakeError('TypeError', 'Failed to define own property: %s'% k) + raise MakeError('TypeError', + 'Failed to define own property: %s' % k) return obj def seal(this, args): @@ -109,14 +117,16 @@ def freeze(this, args): def preventExtensions(this, args): obj = get_arg(args, 0) if not is_object(obj): - raise MakeError('TypeError', 'Object.preventExtensions on non-object') + raise MakeError('TypeError', + 'Object.preventExtensions on non-object') obj.extensible = False return obj def isSealed(this, args): obj = get_arg(args, 0) if not is_object(obj): - raise MakeError('TypeError', 'Object.isSealed called on non-object') + raise MakeError('TypeError', + 'Object.isSealed called on non-object') if obj.extensible: return False for desc in obj.own.values(): @@ -127,7 +137,8 @@ def isSealed(this, args): def isFrozen(this, args): obj = get_arg(args, 0) if not is_object(obj): - raise MakeError('TypeError', 'Object.isFrozen called on non-object') + raise MakeError('TypeError', + 'Object.isFrozen called on non-object') if obj.extensible: return False for desc in obj.own.values(): @@ -140,24 +151,27 @@ def isFrozen(this, args): def isExtensible(this, args): obj = get_arg(args, 0) if not is_object(obj): - raise MakeError('TypeError', 'Object.isExtensible called on non-object') + raise MakeError('TypeError', + 'Object.isExtensible called on non-object') return obj.extensible def keys(this, args): obj = get_arg(args, 0) if not is_object(obj): raise MakeError('TypeError', 'Object.keys called on non-object') - return args.space.ConstructArray([unicode(e) for e,d in six.iteritems(obj.own) if d.get('enumerable')]) - - - + return args.space.ConstructArray([ + unicode(e) for e, d in six.iteritems(obj.own) + if d.get('enumerable') + ]) # some utility functions: + def ToPropertyDescriptor(obj): # page 38 (50 absolute) if not is_object(obj): - raise MakeError('TypeError', 'Can\'t convert non-object to property descriptor') + raise MakeError('TypeError', + 'Can\'t convert non-object to property descriptor') desc = {} if obj.has_property('enumerable'): desc['enumerable'] = to_boolean(obj.get('enumerable')) @@ -166,20 +180,25 @@ def ToPropertyDescriptor(obj): # page 38 (50 absolute) if obj.has_property('value'): desc['value'] = obj.get('value') if obj.has_property('writable'): - desc['writable'] =to_boolean(obj.get('writable')) + desc['writable'] = to_boolean(obj.get('writable')) if obj.has_property('get'): - cand = obj.get('get') + cand = obj.get('get') if not (is_undefined(cand) or is_callable(cand)): - raise MakeError('TypeError', 'Invalid getter (it has to be a function or undefined)') + raise MakeError( + 'TypeError', + 'Invalid getter (it has to be a function or undefined)') desc['get'] = cand if obj.has_property('set'): - cand = obj.get('set') + cand = obj.get('set') if not (is_undefined(cand) or is_callable(cand)): - raise MakeError('TypeError', 'Invalid setter (it has to be a function or undefined)') + raise MakeError( + 'TypeError', + 'Invalid setter (it has to be a function or undefined)') desc['set'] = cand - if ('get' in desc or 'set' in desc) and ('value' in desc or 'writable' in desc): - raise MakeError('TypeError', 'Invalid property. A property cannot both have accessors and be writable or have a value.') + if ('get' in desc or 'set' in desc) and ('value' in desc + or 'writable' in desc): + raise MakeError( + 'TypeError', + 'Invalid property. A property cannot both have accessors and be writable or have a value.' + ) return desc - - - diff --git a/js2py/internals/constructors/jsregexp.py b/js2py/internals/constructors/jsregexp.py index 05b2c2ed..7b264fdb 100644 --- a/js2py/internals/constructors/jsregexp.py +++ b/js2py/internals/constructors/jsregexp.py @@ -3,15 +3,18 @@ from ..func_utils import * from ..base import SpaceTuple - REG_EXP_FLAGS = ('g', 'i', 'm') + def RegExp(this, args): pattern = get_arg(args, 0) flags = get_arg(args, 1) - if GetClass(pattern)=='RegExp': + if GetClass(pattern) == 'RegExp': if not is_undefined(flags): - raise MakeError('TypeError', 'Cannot supply flags when constructing one RegExp from another') + raise MakeError( + 'TypeError', + 'Cannot supply flags when constructing one RegExp from another' + ) # return unchanged return pattern #pattern is not a regexp @@ -22,9 +25,13 @@ def RegExp(this, args): flags = to_string(flags) if not is_undefined(flags) else u'' for flag in flags: if flag not in REG_EXP_FLAGS: - raise MakeError('SyntaxError', 'Invalid flags supplied to RegExp constructor "%s"' % flag) - if len(set(flags))!=len(flags): - raise MakeError('SyntaxError', 'Invalid flags supplied to RegExp constructor "%s"' % flags) + raise MakeError( + 'SyntaxError', + 'Invalid flags supplied to RegExp constructor "%s"' % flag) + if len(set(flags)) != len(flags): + raise MakeError( + 'SyntaxError', + 'Invalid flags supplied to RegExp constructor "%s"' % flags) return args.space.NewRegExp(pattern, flags) @@ -32,4 +39,3 @@ def RegExpCreate(args, space): _args = SpaceTuple(args) _args.space = space return RegExp(undefined, _args) - diff --git a/js2py/internals/constructors/jsstring.py b/js2py/internals/constructors/jsstring.py index e168bf4d..f2b43831 100644 --- a/js2py/internals/constructors/jsstring.py +++ b/js2py/internals/constructors/jsstring.py @@ -8,6 +8,7 @@ def fromCharCode(this, args): res += unichr(to_uint16(e)) return res + def String(this, args): if len(args) == 0: return u'' @@ -18,5 +19,5 @@ def StringConstructor(args, space): temp = space.NewObject() temp.prototype = space.StringPrototype temp.Class = 'String' - temp.value = to_string(get_arg(args, 0)) if len(args)>0 else u'' - return temp \ No newline at end of file + temp.value = to_string(get_arg(args, 0)) if len(args) > 0 else u'' + return temp diff --git a/js2py/internals/constructors/time_helpers.py b/js2py/internals/constructors/time_helpers.py index aba0dd5b..eda95fb6 100644 --- a/js2py/internals/constructors/time_helpers.py +++ b/js2py/internals/constructors/time_helpers.py @@ -8,17 +8,21 @@ try: from tzlocal import get_localzone LOCAL_ZONE = get_localzone() -except: # except all problems... - warnings.warn('Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time') +except: # except all problems... + warnings.warn( + 'Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time' + ) + class LOCAL_ZONE: @staticmethod def dst(*args): return 1 + from js2py.base import MakeError CUM = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) msPerDay = 86400000 -msPerYear = int(86400000*365.242) +msPerYear = int(86400000 * 365.242) msPerSecond = 1000 msPerMinute = 60000 msPerHour = 3600000 @@ -26,66 +30,74 @@ def dst(*args): MinutesPerHour = 60 SecondsPerMinute = 60 NaN = float('nan') -LocalTZA = - time.timezone * msPerSecond - - - +LocalTZA = -time.timezone * msPerSecond def DaylightSavingTA(t): if t is NaN: return t try: - return int(LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(t//1000)).seconds)*1000 + return int( + LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp( + t // 1000)).seconds) * 1000 except: - warnings.warn('Invalid datetime date, assumed DST time, may be inaccurate...', Warning) + warnings.warn( + 'Invalid datetime date, assumed DST time, may be inaccurate...', + Warning) return 1 #raise MakeError('TypeError', 'date not supported by python.datetime. I will solve it in future versions') + def GetTimeZoneName(t): - return time.tzname[DaylightSavingTA(t)>0] + return time.tzname[DaylightSavingTA(t) > 0] + def LocalToUTC(t): return t - LocalTZA - DaylightSavingTA(t - LocalTZA) + def UTCToLocal(t): return t + LocalTZA + DaylightSavingTA(t) def Day(t): - return t//86400000 + return t // 86400000 def TimeWithinDay(t): - return t%86400000 + return t % 86400000 + def DaysInYear(y): - if y%4: + if y % 4: return 365 - elif y%100: + elif y % 100: return 366 - elif y%400: + elif y % 400: return 365 else: return 366 def DayFromYear(y): - return 365 * (y-1970) + (y-1969)//4 -(y-1901)//100 + (y-1601)//400 + return 365 * (y - 1970) + (y - 1969) // 4 - (y - 1901) // 100 + ( + y - 1601) // 400 + def TimeFromYear(y): return 86400000 * DayFromYear(y) + def YearFromTime(t): - guess = 1970 - t//31556908800 # msPerYear + guess = 1970 - t // 31556908800 # msPerYear gt = TimeFromYear(guess) - if gt<=t: - while gt<=t: + if gt <= t: + while gt <= t: guess += 1 gt = TimeFromYear(guess) - return guess-1 + return guess - 1 else: - while gt>t: + while gt > t: guess -= 1 gt = TimeFromYear(guess) return guess @@ -94,73 +106,83 @@ def YearFromTime(t): def DayWithinYear(t): return Day(t) - DayFromYear(YearFromTime(t)) + def InLeapYear(t): y = YearFromTime(t) - if y%4: + if y % 4: return 0 - elif y%100: + elif y % 100: return 1 - elif y%400: + elif y % 400: return 0 else: return 1 + def MonthFromTime(t): day = DayWithinYear(t) leap = InLeapYear(t) - if day<31: + if day < 31: return 0 day -= leap - if day<59: + if day < 59: return 1 - elif day<90: + elif day < 90: return 2 - elif day<120: + elif day < 120: return 3 - elif day<151: + elif day < 151: return 4 - elif day<181: + elif day < 181: return 5 - elif day<212: + elif day < 212: return 6 - elif day<243: + elif day < 243: return 7 - elif day<273: + elif day < 273: return 8 - elif day<304: + elif day < 304: return 9 - elif day<334: + elif day < 334: return 10 else: return 11 + def DateFromTime(t): mon = MonthFromTime(t) day = DayWithinYear(t) - return day-CUM[mon] - (1 if InLeapYear(t) and mon>=2 else 0) + 1 + return day - CUM[mon] - (1 if InLeapYear(t) and mon >= 2 else 0) + 1 + def WeekDay(t): # 0 == sunday return (Day(t) + 4) % 7 + def msFromTime(t): return t % 1000 + def SecFromTime(t): - return (t//1000) % 60 + return (t // 1000) % 60 + def MinFromTime(t): - return (t//60000) % 60 + return (t // 60000) % 60 + def HourFromTime(t): - return (t//3600000) % 24 + return (t // 3600000) % 24 + -def MakeTime (hour, Min, sec, ms): +def MakeTime(hour, Min, sec, ms): # takes PyJs objects and returns t - if not (hour.is_finite() and Min.is_finite() and sec.is_finite() and ms.is_finite()): + if not (hour.is_finite() and Min.is_finite() and sec.is_finite() + and ms.is_finite()): return NaN h, m, s, milli = hour.to_int(), Min.to_int(), sec.to_int(), ms.to_int() - return h*3600000 + m*60000 + s*1000 + milli + return h * 3600000 + m * 60000 + s * 1000 + milli def MakeDay(year, month, date): @@ -168,19 +190,20 @@ def MakeDay(year, month, date): if not (year.is_finite() and month.is_finite() and date.is_finite()): return NaN y, m, dt = year.to_int(), month.to_int(), date.to_int() - y += m//12 + y += m // 12 mn = m % 12 - d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y)==366 and mn>=2 else 0) - return d # ms per day + d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y) == 366 + and mn >= 2 else 0) + return d # ms per day + def MakeDate(day, time): - return 86400000*day + time + return 86400000 * day + time def TimeClip(t): - if t!=t or abs(t)==float('inf'): + if t != t or abs(t) == float('inf'): return NaN - if abs(t) > 8.64 *10**15: + if abs(t) > 8.64 * 10**15: return NaN return int(t) - diff --git a/js2py/internals/conversions.py b/js2py/internals/conversions.py index 6d59f30d..b90a427d 100644 --- a/js2py/internals/conversions.py +++ b/js2py/internals/conversions.py @@ -2,6 +2,7 @@ # Type Conversions. to_type. All must return PyJs subclass instance from simplex import * + def to_primitive(self, hint=None): if is_primitive(self): return self @@ -24,6 +25,7 @@ def to_boolean(self): else: # object - always True return True + def to_number(self): typ = Type(self) if typ == 'Number': # or self.Class=='Number': # no need to convert @@ -76,7 +78,7 @@ def to_string(self): elif is_infinity(self): sign = '-' if self < 0 else '' return sign + 'Infinity' - elif int(self)==self: # integer value! + elif int(self) == self: # integer value! return unicode(int(self)) return unicode(self) # todo make it print exactly like node.js else: # object @@ -88,13 +90,14 @@ def to_object(self, space): if typ == 'Object': return self elif typ == 'Boolean': # Unsure ... todo check here - return space.Boolean.create((self,), space) + return space.Boolean.create((self, ), space) elif typ == 'Number': # ? - return space.Number.create((self,), space) + return space.Number.create((self, ), space) elif typ == 'String': # ? - return space.String.create((self,), space) + return space.String.create((self, ), space) elif typ == 'Null' or typ == 'Undefined': - raise MakeError('TypeError', 'undefined or null can\'t be converted to object') + raise MakeError('TypeError', + 'undefined or null can\'t be converted to object') else: raise RuntimeError() @@ -103,8 +106,8 @@ def to_int32(self): num = to_number(self) if is_nan(num) or is_infinity(num): return 0 - int32 = int(num) % 2 ** 32 - return int(int32 - 2 ** 32 if int32 >= 2 ** 31 else int32) + int32 = int(num) % 2**32 + return int(int32 - 2**32 if int32 >= 2**31 else int32) def to_int(self): @@ -112,7 +115,7 @@ def to_int(self): if is_nan(num): return 0 elif is_infinity(num): - return 10 ** 20 if num > 0 else -10 ** 20 + return 10**20 if num > 0 else -10**20 return int(num) @@ -120,25 +123,26 @@ def to_uint32(self): num = to_number(self) if is_nan(num) or is_infinity(num): return 0 - return int(num) % 2 ** 32 + return int(num) % 2**32 def to_uint16(self): num = to_number(self) if is_nan(num) or is_infinity(num): return 0 - return int(num) % 2 ** 16 + return int(num) % 2**16 def to_int16(self): num = to_number(self) if is_nan(num) or is_infinity(num): return 0 - int16 = int(num) % 2 ** 16 - return int(int16 - 2 ** 16 if int16 >= 2 ** 15 else int16) + int16 = int(num) % 2**16 + return int(int16 - 2**16 if int16 >= 2**15 else int16) def cok(self): """Check object coercible""" if type(self) in (UNDEFINED_TYPE, NULL_TYPE): - raise MakeError('TypeError', 'undefined or null can\'t be converted to object') \ No newline at end of file + raise MakeError('TypeError', + 'undefined or null can\'t be converted to object') diff --git a/js2py/internals/desc.py b/js2py/internals/desc.py index 5e29b3f8..e81179c2 100644 --- a/js2py/internals/desc.py +++ b/js2py/internals/desc.py @@ -4,6 +4,7 @@ # Warning: value, get, set props of dest are PyJs types. Rest is Py! + def is_data_descriptor(desc): return desc and ('value' in desc or 'writable' in desc) @@ -12,8 +13,11 @@ def is_accessor_descriptor(desc): return desc and ('get' in desc or 'set' in desc) -def is_generic_descriptor(desc): # generic means not the data and not the setter - therefore it must be one that changes only enum and conf - return desc and not (is_data_descriptor(desc) or is_accessor_descriptor(desc)) +def is_generic_descriptor( + desc +): # generic means not the data and not the setter - therefore it must be one that changes only enum and conf + return desc and not (is_data_descriptor(desc) + or is_accessor_descriptor(desc)) def from_property_descriptor(desc, space): @@ -21,36 +25,54 @@ def from_property_descriptor(desc, space): return {} obj = space.NewObject() if is_data_descriptor(desc): - obj.define_own_property('value', {'value': desc['value'], - 'writable': True, - 'enumerable': True, - 'configurable': True}, False) - obj.define_own_property('writable', {'value': desc['writable'], - 'writable': True, - 'enumerable': True, - 'configurable': True}, False) + obj.define_own_property( + 'value', { + 'value': desc['value'], + 'writable': True, + 'enumerable': True, + 'configurable': True + }, False) + obj.define_own_property( + 'writable', { + 'value': desc['writable'], + 'writable': True, + 'enumerable': True, + 'configurable': True + }, False) else: - obj.define_own_property('get', {'value': desc['get'], - 'writable': True, - 'enumerable': True, - 'configurable': True}, False) - obj.define_own_property('set', {'value': desc['set'], - 'writable': True, - 'enumerable': True, - 'configurable': True}, False) - obj.define_own_property('writable', {'value': desc['writable'], - 'writable': True, - 'enumerable': True, - 'configurable': True}, False) - obj.define_own_property('enumerable', {'value': desc['enumerable'], - 'writable': True, - 'enumerable': True, - 'configurable': True}, False) + obj.define_own_property( + 'get', { + 'value': desc['get'], + 'writable': True, + 'enumerable': True, + 'configurable': True + }, False) + obj.define_own_property( + 'set', { + 'value': desc['set'], + 'writable': True, + 'enumerable': True, + 'configurable': True + }, False) + obj.define_own_property( + 'writable', { + 'value': desc['writable'], + 'writable': True, + 'enumerable': True, + 'configurable': True + }, False) + obj.define_own_property( + 'enumerable', { + 'value': desc['enumerable'], + 'writable': True, + 'enumerable': True, + 'configurable': True + }, False) return obj def to_property_descriptor(obj): - if obj._type()!='Object': + if obj._type() != 'Object': raise TypeError() desc = {} for e in ('enumerable', 'configurable', 'writable'): @@ -63,6 +85,6 @@ def to_property_descriptor(obj): cand = obj.get(e) if not (cand.is_callable() or cand.is_undefined()): raise TypeError() - if ('get' in desc or 'set' in desc) and ('value' in desc or 'writable' in desc): + if ('get' in desc or 'set' in desc) and ('value' in desc + or 'writable' in desc): raise TypeError() - diff --git a/js2py/internals/esprima.js b/js2py/internals/esprima.js deleted file mode 100644 index 15105546..00000000 --- a/js2py/internals/esprima.js +++ /dev/null @@ -1,5664 +0,0 @@ -/* - Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -(function (root, factory) { - - // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, - // Rhino, and plain browser loading. - - /* istanbul ignore next */ - if (typeof define === 'function' && define.amd) { - define(['exports'], factory); - } else if (typeof exports !== 'undefined') { - factory(exports); - } else { - factory((root.esprima = {})); - } -}(this, function (exports) { - - var Token, - TokenName, - FnExprTokens, - Syntax, - PlaceHolders, - Messages, - Regex, - source, - strict, - index, - lineNumber, - lineStart, - hasLineTerminator, - lastIndex, - lastLineNumber, - lastLineStart, - startIndex, - startLineNumber, - startLineStart, - scanning, - length, - lookahead, - state, - extra, - isBindingElement, - isAssignmentTarget, - firstCoverInitializedNameError; - - Token = { - BooleanLiteral: 1, - EOF: 2, - Identifier: 3, - Keyword: 4, - NullLiteral: 5, - NumericLiteral: 6, - Punctuator: 7, - StringLiteral: 8, - RegularExpression: 9, - Template: 10 - }; - - TokenName = {}; - TokenName[Token.BooleanLiteral] = 'Boolean'; - TokenName[Token.EOF] = ''; - TokenName[Token.Identifier] = 'Identifier'; - TokenName[Token.Keyword] = 'Keyword'; - TokenName[Token.NullLiteral] = 'Null'; - TokenName[Token.NumericLiteral] = 'Numeric'; - TokenName[Token.Punctuator] = 'Punctuator'; - TokenName[Token.StringLiteral] = 'String'; - TokenName[Token.RegularExpression] = 'RegularExpression'; - TokenName[Token.Template] = 'Template'; - - // A function following one of those tokens is an expression. - FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new', - 'return', 'case', 'delete', 'throw', 'void', - // assignment operators - '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', - '&=', '|=', '^=', ',', - // binary/unary operators - '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&', - '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=', - '<=', '<', '>', '!=', '!==']; - - Syntax = { - AssignmentExpression: 'AssignmentExpression', - AssignmentPattern: 'AssignmentPattern', - ArrayExpression: 'ArrayExpression', - ArrayPattern: 'ArrayPattern', - ArrowFunctionExpression: 'ArrowFunctionExpression', - BlockStatement: 'BlockStatement', - BinaryExpression: 'BinaryExpression', - BreakStatement: 'BreakStatement', - CallExpression: 'CallExpression', - CatchClause: 'CatchClause', - ClassBody: 'ClassBody', - ClassDeclaration: 'ClassDeclaration', - ClassExpression: 'ClassExpression', - ConditionalExpression: 'ConditionalExpression', - ContinueStatement: 'ContinueStatement', - DoWhileStatement: 'DoWhileStatement', - DebuggerStatement: 'DebuggerStatement', - EmptyStatement: 'EmptyStatement', - ExportAllDeclaration: 'ExportAllDeclaration', - ExportDefaultDeclaration: 'ExportDefaultDeclaration', - ExportNamedDeclaration: 'ExportNamedDeclaration', - ExportSpecifier: 'ExportSpecifier', - ExpressionStatement: 'ExpressionStatement', - ForStatement: 'ForStatement', - ForOfStatement: 'ForOfStatement', - ForInStatement: 'ForInStatement', - FunctionDeclaration: 'FunctionDeclaration', - FunctionExpression: 'FunctionExpression', - Identifier: 'Identifier', - IfStatement: 'IfStatement', - ImportDeclaration: 'ImportDeclaration', - ImportDefaultSpecifier: 'ImportDefaultSpecifier', - ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', - ImportSpecifier: 'ImportSpecifier', - Literal: 'Literal', - LabeledStatement: 'LabeledStatement', - LogicalExpression: 'LogicalExpression', - MemberExpression: 'MemberExpression', - MetaProperty: 'MetaProperty', - MethodDefinition: 'MethodDefinition', - NewExpression: 'NewExpression', - ObjectExpression: 'ObjectExpression', - ObjectPattern: 'ObjectPattern', - Program: 'Program', - Property: 'Property', - RestElement: 'RestElement', - ReturnStatement: 'ReturnStatement', - SequenceExpression: 'SequenceExpression', - SpreadElement: 'SpreadElement', - Super: 'Super', - SwitchCase: 'SwitchCase', - SwitchStatement: 'SwitchStatement', - TaggedTemplateExpression: 'TaggedTemplateExpression', - TemplateElement: 'TemplateElement', - TemplateLiteral: 'TemplateLiteral', - ThisExpression: 'ThisExpression', - ThrowStatement: 'ThrowStatement', - TryStatement: 'TryStatement', - UnaryExpression: 'UnaryExpression', - UpdateExpression: 'UpdateExpression', - VariableDeclaration: 'VariableDeclaration', - VariableDeclarator: 'VariableDeclarator', - WhileStatement: 'WhileStatement', - WithStatement: 'WithStatement', - YieldExpression: 'YieldExpression' - }; - - PlaceHolders = { - ArrowParameterPlaceHolder: 'ArrowParameterPlaceHolder' - }; - - // Error messages should be identical to V8. - Messages = { - UnexpectedToken: 'Unexpected token %0', - UnexpectedNumber: 'Unexpected number', - UnexpectedString: 'Unexpected string', - UnexpectedIdentifier: 'Unexpected identifier', - UnexpectedReserved: 'Unexpected reserved word', - UnexpectedTemplate: 'Unexpected quasi %0', - UnexpectedEOS: 'Unexpected end of input', - NewlineAfterThrow: 'Illegal newline after throw', - InvalidRegExp: 'Invalid regular expression', - UnterminatedRegExp: 'Invalid regular expression: missing /', - InvalidLHSInAssignment: 'Invalid left-hand side in assignment', - InvalidLHSInForIn: 'Invalid left-hand side in for-in', - InvalidLHSInForLoop: 'Invalid left-hand side in for-loop', - MultipleDefaultsInSwitch: 'More than one default clause in switch statement', - NoCatchOrFinally: 'Missing catch or finally after try', - UnknownLabel: 'Undefined label \'%0\'', - Redeclaration: '%0 \'%1\' has already been declared', - IllegalContinue: 'Illegal continue statement', - IllegalBreak: 'Illegal break statement', - IllegalReturn: 'Illegal return statement', - StrictModeWith: 'Strict mode code may not include a with statement', - StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', - StrictVarName: 'Variable name may not be eval or arguments in strict mode', - StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', - StrictParamDupe: 'Strict mode function may not have duplicate parameter names', - StrictFunctionName: 'Function name may not be eval or arguments in strict mode', - StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', - StrictDelete: 'Delete of an unqualified identifier in strict mode.', - StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', - StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', - StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', - StrictReservedWord: 'Use of future reserved word in strict mode', - TemplateOctalLiteral: 'Octal literals are not allowed in template strings.', - ParameterAfterRestParameter: 'Rest parameter must be last formal parameter', - DefaultRestParameter: 'Unexpected token =', - ObjectPatternAsRestParameter: 'Unexpected token {', - DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals', - ConstructorSpecialMethod: 'Class constructor may not be an accessor', - DuplicateConstructor: 'A class may only have one constructor', - StaticPrototype: 'Classes may not have static property named prototype', - MissingFromClause: 'Unexpected token', - NoAsAfterImportNamespace: 'Unexpected token', - InvalidModuleSpecifier: 'Unexpected token', - IllegalImportDeclaration: 'Unexpected token', - IllegalExportDeclaration: 'Unexpected token', - DuplicateBinding: 'Duplicate binding %0' - }; - - // See also tools/generate-unicode-regex.js. - Regex = { - // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierStart: - NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B2\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDE00-\uDE11\uDE13-\uDE2B\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDE00-\uDE2F\uDE44\uDE80-\uDEAA]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]/, - - // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierPart: - NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B2\u08E4-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA69D\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2D\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDD0-\uDDDA\uDE00-\uDE11\uDE13-\uDE37\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF01-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/ - }; - - // Ensure the condition is true, otherwise throw an error. - // This is only to have a better contract semantic, i.e. another safety net - // to catch a logic error. The condition shall be fulfilled in normal case. - // Do NOT use this to enforce a certain condition on any user input. - - function assert(condition, message) { - /* istanbul ignore if */ - if (!condition) { - throw new Error('ASSERT: ' + message); - } - } - - function isDecimalDigit(ch) { - log(NaN >= 10) - return (ch >= 0x30 && ch <= 0x39); // 0..9 - } - - function isHexDigit(ch) { - return '0123456789abcdefABCDEF'.indexOf(ch) >= 0; - } - - function isOctalDigit(ch) { - return '01234567'.indexOf(ch) >= 0; - } - - function octalToDecimal(ch) { - // \0 is not octal escape sequence - var octal = (ch !== '0'), code = '01234567'.indexOf(ch); - - if (index < length && isOctalDigit(source[index])) { - octal = true; - code = code * 8 + '01234567'.indexOf(source[index++]); - - // 3 digits are only allowed when string starts - // with 0, 1, 2, 3 - if ('0123'.indexOf(ch) >= 0 && - index < length && - isOctalDigit(source[index])) { - code = code * 8 + '01234567'.indexOf(source[index++]); - } - } - - return { - code: code, - octal: octal - }; - } - - // ECMA-262 11.2 White Space - - function isWhiteSpace(ch) { - return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || - (ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0); - } - - // ECMA-262 11.3 Line Terminators - - function isLineTerminator(ch) { - return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029); - } - - // ECMA-262 11.6 Identifier Names and Identifiers - - function fromCodePoint(cp) { - return (cp < 0x10000) ? String.fromCharCode(cp) : - String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) + - String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023)); - } - - function isIdentifierStart(ch) { - return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) - (ch >= 0x41 && ch <= 0x5A) || // A..Z - (ch >= 0x61 && ch <= 0x7A) || // a..z - (ch === 0x5C) || // \ (backslash) - ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch))); - } - - function isIdentifierPart(ch) { - return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) - (ch >= 0x41 && ch <= 0x5A) || // A..Z - (ch >= 0x61 && ch <= 0x7A) || // a..z - (ch >= 0x30 && ch <= 0x39) || // 0..9 - (ch === 0x5C) || // \ (backslash) - ((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch))); - } - - // ECMA-262 11.6.2.2 Future Reserved Words - - function isFutureReservedWord(id) { - switch (id) { - case 'enum': - case 'export': - case 'import': - case 'super': - return true; - default: - return false; - } - } - - function isStrictModeReservedWord(id) { - switch (id) { - case 'implements': - case 'interface': - case 'package': - case 'private': - case 'protected': - case 'public': - case 'static': - case 'yield': - case 'let': - return true; - default: - return false; - } - } - - function isRestrictedWord(id) { - return id === 'eval' || id === 'arguments'; - } - - // ECMA-262 11.6.2.1 Keywords - - function isKeyword(id) { - - // 'const' is specialized as Keyword in V8. - // 'yield' and 'let' are for compatibility with SpiderMonkey and ES.next. - // Some others are from future reserved words. - - switch (id.length) { - case 2: - return (id === 'if') || (id === 'in') || (id === 'do'); - case 3: - return (id === 'var') || (id === 'for') || (id === 'new') || - (id === 'try') || (id === 'let'); - case 4: - return (id === 'this') || (id === 'else') || (id === 'case') || - (id === 'void') || (id === 'with') || (id === 'enum'); - case 5: - return (id === 'while') || (id === 'break') || (id === 'catch') || - (id === 'throw') || (id === 'const') || (id === 'yield') || - (id === 'class') || (id === 'super'); - case 6: - return (id === 'return') || (id === 'typeof') || (id === 'delete') || - (id === 'switch') || (id === 'export') || (id === 'import'); - case 7: - return (id === 'default') || (id === 'finally') || (id === 'extends'); - case 8: - return (id === 'function') || (id === 'continue') || (id === 'debugger'); - case 10: - return (id === 'instanceof'); - default: - return false; - } - } - - // ECMA-262 11.4 Comments - - function addComment(type, value, start, end, loc) { - var comment; - - assert(typeof start === 'number', 'Comment must have valid position'); - - state.lastCommentStart = start; - - comment = { - type: type, - value: value - }; - if (extra.range) { - comment.range = [start, end]; - } - if (extra.loc) { - comment.loc = loc; - } - extra.comments.push(comment); - if (extra.attachComment) { - extra.leadingComments.push(comment); - extra.trailingComments.push(comment); - } - } - - function skipSingleLineComment(offset) { - var start, loc, ch, comment; - - start = index - offset; - loc = { - start: { - line: lineNumber, - column: index - lineStart - offset - } - }; - - while (index < length) { - ch = source.charCodeAt(index); - ++index; - if (isLineTerminator(ch)) { - hasLineTerminator = true; - if (extra.comments) { - comment = source.slice(start + offset, index - 1); - loc.end = { - line: lineNumber, - column: index - lineStart - 1 - }; - addComment('Line', comment, start, index - 1, loc); - } - if (ch === 13 && source.charCodeAt(index) === 10) { - ++index; - } - ++lineNumber; - lineStart = index; - return; - } - } - - if (extra.comments) { - comment = source.slice(start + offset, index); - loc.end = { - line: lineNumber, - column: index - lineStart - }; - addComment('Line', comment, start, index, loc); - } - } - - function skipMultiLineComment() { - var start, loc, ch, comment; - - if (extra.comments) { - start = index - 2; - loc = { - start: { - line: lineNumber, - column: index - lineStart - 2 - } - }; - } - - while (index < length) { - ch = source.charCodeAt(index); - if (isLineTerminator(ch)) { - if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) { - ++index; - } - hasLineTerminator = true; - ++lineNumber; - ++index; - lineStart = index; - } else if (ch === 0x2A) { - // Block comment ends with '*/'. - if (source.charCodeAt(index + 1) === 0x2F) { - ++index; - ++index; - if (extra.comments) { - comment = source.slice(start + 2, index - 2); - loc.end = { - line: lineNumber, - column: index - lineStart - }; - addComment('Block', comment, start, index, loc); - } - return; - } - ++index; - } else { - ++index; - } - } - - // Ran off the end of the file - the whole thing is a comment - if (extra.comments) { - loc.end = { - line: lineNumber, - column: index - lineStart - }; - comment = source.slice(start + 2, index); - addComment('Block', comment, start, index, loc); - } - tolerateUnexpectedToken(); - } - - function skipComment() { - var ch, start; - hasLineTerminator = false; - - start = (index === 0); - while (index < length) { - ch = source.charCodeAt(index); - - if (isWhiteSpace(ch)) { - ++index; - } else if (isLineTerminator(ch)) { - hasLineTerminator = true; - ++index; - if (ch === 0x0D && source.charCodeAt(index) === 0x0A) { - ++index; - } - ++lineNumber; - lineStart = index; - start = true; - } else if (ch === 0x2F) { // U+002F is '/' - ch = source.charCodeAt(index + 1); - if (ch === 0x2F) { - ++index; - ++index; - skipSingleLineComment(2); - start = true; - } else if (ch === 0x2A) { // U+002A is '*' - ++index; - ++index; - skipMultiLineComment(); - } else { - break; - } - } else if (start && ch === 0x2D) { // U+002D is '-' - // U+003E is '>' - if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) { - // '-->' is a single-line comment - index += 3; - skipSingleLineComment(3); - } else { - break; - } - } else if (ch === 0x3C) { // U+003C is '<' - if (source.slice(index + 1, index + 4) === '!--') { - ++index; // `<` - ++index; // `!` - ++index; // `-` - ++index; // `-` - skipSingleLineComment(4); - } else { - break; - } - } else { - break; - } - } - } - - function scanHexEscape(prefix) { - var i, len, ch, code = 0; - - len = (prefix === 'u') ? 4 : 2; - for (i = 0; i < len; ++i) { - if (index < length && isHexDigit(source[index])) { - ch = source[index++]; - code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); - } else { - return ''; - } - } - return String.fromCharCode(code); - } - - function scanUnicodeCodePointEscape() { - var ch, code; - - ch = source[index]; - code = 0; - - // At least, one hex digit is required. - if (ch === '}') { - throwUnexpectedToken(); - } - - while (index < length) { - ch = source[index++]; - if (!isHexDigit(ch)) { - break; - } - code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); - } - - if (code > 0x10FFFF || ch !== '}') { - throwUnexpectedToken(); - } - - return fromCodePoint(code); - } - - function codePointAt(i) { - var cp, first, second; - - cp = source.charCodeAt(i); - if (cp >= 0xD800 && cp <= 0xDBFF) { - second = source.charCodeAt(i + 1); - if (second >= 0xDC00 && second <= 0xDFFF) { - first = cp; - cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; - } - } - - return cp; - } - - function getComplexIdentifier() { - var cp, ch, id; - - cp = codePointAt(index); - id = fromCodePoint(cp); - index += id.length; - - // '\u' (U+005C, U+0075) denotes an escaped character. - if (cp === 0x5C) { - if (source.charCodeAt(index) !== 0x75) { - throwUnexpectedToken(); - } - ++index; - if (source[index] === '{') { - ++index; - ch = scanUnicodeCodePointEscape(); - } else { - ch = scanHexEscape('u'); - cp = ch.charCodeAt(0); - if (!ch || ch === '\\' || !isIdentifierStart(cp)) { - throwUnexpectedToken(); - } - } - id = ch; - } - - while (index < length) { - cp = codePointAt(index); - if (!isIdentifierPart(cp)) { - break; - } - ch = fromCodePoint(cp); - id += ch; - index += ch.length; - - // '\u' (U+005C, U+0075) denotes an escaped character. - if (cp === 0x5C) { - id = id.substr(0, id.length - 1); - if (source.charCodeAt(index) !== 0x75) { - throwUnexpectedToken(); - } - ++index; - if (source[index] === '{') { - ++index; - ch = scanUnicodeCodePointEscape(); - } else { - ch = scanHexEscape('u'); - cp = ch.charCodeAt(0); - if (!ch || ch === '\\' || !isIdentifierPart(cp)) { - throwUnexpectedToken(); - } - } - id += ch; - } - } - - return id; - } - - function getIdentifier() { - var start, ch; - - start = index++; - while (index < length) { - ch = source.charCodeAt(index); - if (ch === 0x5C) { - // Blackslash (U+005C) marks Unicode escape sequence. - index = start; - return getComplexIdentifier(); - } else if (ch >= 0xD800 && ch < 0xDFFF) { - // Need to handle surrogate pairs. - index = start; - return getComplexIdentifier(); - } - if (isIdentifierPart(ch)) { - ++index; - } else { - break; - } - } - - return source.slice(start, index); - } - - function scanIdentifier() { - var start, id, type; - - start = index; - - // Backslash (U+005C) starts an escaped character. - id = (source.charCodeAt(index) === 0x5C) ? getComplexIdentifier() : getIdentifier(); - - // There is no keyword or literal with only one character. - // Thus, it must be an identifier. - if (id.length === 1) { - type = Token.Identifier; - } else if (isKeyword(id)) { - type = Token.Keyword; - } else if (id === 'null') { - type = Token.NullLiteral; - } else if (id === 'true' || id === 'false') { - type = Token.BooleanLiteral; - } else { - type = Token.Identifier; - } - - return { - type: type, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - start: start, - end: index - }; - } - - - // ECMA-262 11.7 Punctuators - - function scanPunctuator() { - var token, str; - - token = { - type: Token.Punctuator, - value: '', - lineNumber: lineNumber, - lineStart: lineStart, - start: index, - end: index - }; - - // Check for most common single-character punctuators. - str = source[index]; - switch (str) { - - case '(': - if (extra.tokenize) { - extra.openParenToken = extra.tokens.length; - } - ++index; - break; - - case '{': - if (extra.tokenize) { - extra.openCurlyToken = extra.tokens.length; - } - state.curlyStack.push('{'); - ++index; - break; - - case '.': - ++index; - if (source[index] === '.' && source[index + 1] === '.') { - // Spread operator: ... - index += 2; - str = '...'; - } - break; - - case '}': - ++index; - state.curlyStack.pop(); - break; - case ')': - case ';': - case ',': - case '[': - case ']': - case ':': - case '?': - case '~': - ++index; - break; - - default: - // 4-character punctuator. - str = source.substr(index, 4); - if (str === '>>>=') { - index += 4; - } else { - - // 3-character punctuators. - str = str.substr(0, 3); - if (str === '===' || str === '!==' || str === '>>>' || - str === '<<=' || str === '>>=') { - index += 3; - } else { - - // 2-character punctuators. - str = str.substr(0, 2); - if (str === '&&' || str === '||' || str === '==' || str === '!=' || - str === '+=' || str === '-=' || str === '*=' || str === '/=' || - str === '++' || str === '--' || str === '<<' || str === '>>' || - str === '&=' || str === '|=' || str === '^=' || str === '%=' || - str === '<=' || str === '>=' || str === '=>') { - index += 2; - } else { - - // 1-character punctuators. - str = source[index]; - if ('<>=!+-*%&|^/'.indexOf(str) >= 0) { - ++index; - } - } - } - } - } - - if (index === token.start) { - throwUnexpectedToken(); - } - - token.end = index; - token.value = str; - return token; - } - - // ECMA-262 11.8.3 Numeric Literals - - function scanHexLiteral(start) { - var number = ''; - - while (index < length) { - if (!isHexDigit(source[index])) { - break; - } - number += source[index++]; - } - - if (number.length === 0) { - throwUnexpectedToken(); - } - - if (isIdentifierStart(source.charCodeAt(index))) { - throwUnexpectedToken(); - } - - return { - type: Token.NumericLiteral, - value: parseInt('0x' + number, 16), - lineNumber: lineNumber, - lineStart: lineStart, - start: start, - end: index - }; - } - - function scanBinaryLiteral(start) { - var ch, number; - - number = ''; - - while (index < length) { - ch = source[index]; - if (ch !== '0' && ch !== '1') { - break; - } - number += source[index++]; - } - - if (number.length === 0) { - // only 0b or 0B - throwUnexpectedToken(); - } - - if (index < length) { - ch = source.charCodeAt(index); - /* istanbul ignore else */ - if (isIdentifierStart(ch) || isDecimalDigit(ch)) { - throwUnexpectedToken(); - } - } - - return { - type: Token.NumericLiteral, - value: parseInt(number, 2), - lineNumber: lineNumber, - lineStart: lineStart, - start: start, - end: index - }; - } - - function scanOctalLiteral(prefix, start) { - var number, octal; - - if (isOctalDigit(prefix)) { - octal = true; - number = '0' + source[index++]; - } else { - octal = false; - ++index; - number = ''; - } - - while (index < length) { - if (!isOctalDigit(source[index])) { - break; - } - number += source[index++]; - } - - if (!octal && number.length === 0) { - // only 0o or 0O - throwUnexpectedToken(); - } - - if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) { - throwUnexpectedToken(); - } - - return { - type: Token.NumericLiteral, - value: parseInt(number, 8), - octal: octal, - lineNumber: lineNumber, - lineStart: lineStart, - start: start, - end: index - }; - } - - function isImplicitOctalLiteral() { - var i, ch; - - // Implicit octal, unless there is a non-octal digit. - // (Annex B.1.1 on Numeric Literals) - for (i = index + 1; i < length; ++i) { - ch = source[i]; - if (ch === '8' || ch === '9') { - return false; - } - if (!isOctalDigit(ch)) { - return true; - } - } - - return true; - } - - function scanNumericLiteral() { - var number, start, ch; - - ch = source[index]; - assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), - 'Numeric literal must start with a decimal digit or a decimal point'); - - start = index; - number = ''; - if (ch !== '.') { - number = source[index++]; - ch = source[index]; - - // Hex number starts with '0x'. - // Octal number starts with '0'. - // Octal number in ES6 starts with '0o'. - // Binary number in ES6 starts with '0b'. - if (number === '0') { - if (ch === 'x' || ch === 'X') { - ++index; - return scanHexLiteral(start); - } - if (ch === 'b' || ch === 'B') { - ++index; - return scanBinaryLiteral(start); - } - if (ch === 'o' || ch === 'O') { - return scanOctalLiteral(ch, start); - } - - if (isOctalDigit(ch)) { - if (isImplicitOctalLiteral()) { - return scanOctalLiteral(ch, start); - } - } - } - - while (isDecimalDigit(source.charCodeAt(index))) { - log(index) - number += source[index++]; - } - ch = source[index]; - } - - if (ch === '.') { - number += source[index++]; - while (isDecimalDigit(source.charCodeAt(index))) { - number += source[index++]; - } - ch = source[index]; - } - - if (ch === 'e' || ch === 'E') { - number += source[index++]; - - ch = source[index]; - if (ch === '+' || ch === '-') { - number += source[index++]; - } - if (isDecimalDigit(source.charCodeAt(index))) { - while (isDecimalDigit(source.charCodeAt(index))) { - number += source[index++]; - } - } else { - throwUnexpectedToken(); - } - } - - if (isIdentifierStart(source.charCodeAt(index))) { - throwUnexpectedToken(); - } - - return { - type: Token.NumericLiteral, - value: parseFloat(number), - lineNumber: lineNumber, - lineStart: lineStart, - start: start, - end: index - }; - } - - // ECMA-262 11.8.4 String Literals - - function scanStringLiteral() { - var str = '', quote, start, ch, unescaped, octToDec, octal = false; - - quote = source[index]; - assert((quote === '\'' || quote === '"'), - 'String literal must starts with a quote'); - - start = index; - ++index; - - while (index < length) { - ch = source[index++]; - - if (ch === quote) { - quote = ''; - break; - } else if (ch === '\\') { - ch = source[index++]; - if (!ch || !isLineTerminator(ch.charCodeAt(0))) { - switch (ch) { - case 'u': - case 'x': - if (source[index] === '{') { - ++index; - str += scanUnicodeCodePointEscape(); - } else { - unescaped = scanHexEscape(ch); - if (!unescaped) { - throw throwUnexpectedToken(); - } - str += unescaped; - } - break; - case 'n': - str += '\n'; - break; - case 'r': - str += '\r'; - break; - case 't': - str += '\t'; - break; - case 'b': - str += '\b'; - break; - case 'f': - str += '\f'; - break; - case 'v': - str += '\x0B'; - break; - case '8': - case '9': - str += ch; - tolerateUnexpectedToken(); - break; - - default: - if (isOctalDigit(ch)) { - octToDec = octalToDecimal(ch); - - octal = octToDec.octal || octal; - str += String.fromCharCode(octToDec.code); - } else { - str += ch; - } - break; - } - } else { - ++lineNumber; - if (ch === '\r' && source[index] === '\n') { - ++index; - } - lineStart = index; - } - } else if (isLineTerminator(ch.charCodeAt(0))) { - break; - } else { - str += ch; - } - } - - if (quote !== '') { - throwUnexpectedToken(); - } - - return { - type: Token.StringLiteral, - value: str, - octal: octal, - lineNumber: startLineNumber, - lineStart: startLineStart, - start: start, - end: index - }; - } - - // ECMA-262 11.8.6 Template Literal Lexical Components - - function scanTemplate() { - var cooked = '', ch, start, rawOffset, terminated, head, tail, restore, unescaped; - - terminated = false; - tail = false; - start = index; - head = (source[index] === '`'); - rawOffset = 2; - - ++index; - - while (index < length) { - ch = source[index++]; - if (ch === '`') { - rawOffset = 1; - tail = true; - terminated = true; - break; - } else if (ch === '$') { - if (source[index] === '{') { - state.curlyStack.push('${'); - ++index; - terminated = true; - break; - } - cooked += ch; - } else if (ch === '\\') { - ch = source[index++]; - if (!isLineTerminator(ch.charCodeAt(0))) { - switch (ch) { - case 'n': - cooked += '\n'; - break; - case 'r': - cooked += '\r'; - break; - case 't': - cooked += '\t'; - break; - case 'u': - case 'x': - if (source[index] === '{') { - ++index; - cooked += scanUnicodeCodePointEscape(); - } else { - restore = index; - unescaped = scanHexEscape(ch); - if (unescaped) { - cooked += unescaped; - } else { - index = restore; - cooked += ch; - } - } - break; - case 'b': - cooked += '\b'; - break; - case 'f': - cooked += '\f'; - break; - case 'v': - cooked += '\v'; - break; - - default: - if (ch === '0') { - if (isDecimalDigit(source.charCodeAt(index))) { - // Illegal: \01 \02 and so on - throwError(Messages.TemplateOctalLiteral); - } - cooked += '\0'; - } else if (isOctalDigit(ch)) { - // Illegal: \1 \2 - throwError(Messages.TemplateOctalLiteral); - } else { - cooked += ch; - } - break; - } - } else { - ++lineNumber; - if (ch === '\r' && source[index] === '\n') { - ++index; - } - lineStart = index; - } - } else if (isLineTerminator(ch.charCodeAt(0))) { - ++lineNumber; - if (ch === '\r' && source[index] === '\n') { - ++index; - } - lineStart = index; - cooked += '\n'; - } else { - cooked += ch; - } - } - - if (!terminated) { - throwUnexpectedToken(); - } - - if (!head) { - state.curlyStack.pop(); - } - - return { - type: Token.Template, - value: { - cooked: cooked, - raw: source.slice(start + 1, index - rawOffset) - }, - head: head, - tail: tail, - lineNumber: lineNumber, - lineStart: lineStart, - start: start, - end: index - }; - } - - // ECMA-262 11.8.5 Regular Expression Literals - - function testRegExp(pattern, flags) { - // The BMP character to use as a replacement for astral symbols when - // translating an ES6 "u"-flagged pattern to an ES5-compatible - // approximation. - // Note: replacing with '\uFFFF' enables false positives in unlikely - // scenarios. For example, `[\u{1044f}-\u{10440}]` is an invalid - // pattern that would not be detected by this substitution. - var astralSubstitute = '\uFFFF', - tmp = pattern; - - if (flags.indexOf('u') >= 0) { - tmp = tmp - // Replace every Unicode escape sequence with the equivalent - // BMP character or a constant ASCII code point in the case of - // astral symbols. (See the above note on `astralSubstitute` - // for more information.) - .replace(/\\u\{([0-9a-fA-F]+)\}|\\u([a-fA-F0-9]{4})/g, function ($0, $1, $2) { - var codePoint = parseInt($1 || $2, 16); - if (codePoint > 0x10FFFF) { - throwUnexpectedToken(null, Messages.InvalidRegExp); - } - if (codePoint <= 0xFFFF) { - return String.fromCharCode(codePoint); - } - return astralSubstitute; - }) - // Replace each paired surrogate with a single ASCII symbol to - // avoid throwing on regular expressions that are only valid in - // combination with the "u" flag. - .replace( - /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, - astralSubstitute - ); - } - - // First, detect invalid regular expressions. - try { - RegExp(tmp); - } catch (e) { - throwUnexpectedToken(null, Messages.InvalidRegExp); - } - - // Return a regular expression object for this pattern-flag pair, or - // `null` in case the current environment doesn't support the flags it - // uses. - try { - return new RegExp(pattern, flags); - } catch (exception) { - return null; - } - } - - function scanRegExpBody() { - var ch, str, classMarker, terminated, body; - - ch = source[index]; - assert(ch === '/', 'Regular expression literal must start with a slash'); - str = source[index++]; - - classMarker = false; - terminated = false; - while (index < length) { - ch = source[index++]; - str += ch; - if (ch === '\\') { - ch = source[index++]; - // ECMA-262 7.8.5 - if (isLineTerminator(ch.charCodeAt(0))) { - throwUnexpectedToken(null, Messages.UnterminatedRegExp); - } - str += ch; - } else if (isLineTerminator(ch.charCodeAt(0))) { - throwUnexpectedToken(null, Messages.UnterminatedRegExp); - } else if (classMarker) { - if (ch === ']') { - classMarker = false; - } - } else { - if (ch === '/') { - terminated = true; - break; - } else if (ch === '[') { - classMarker = true; - } - } - } - - if (!terminated) { - throwUnexpectedToken(null, Messages.UnterminatedRegExp); - } - - // Exclude leading and trailing slash. - body = str.substr(1, str.length - 2); - return { - value: body, - literal: str - }; - } - - function scanRegExpFlags() { - var ch, str, flags, restore; - - str = ''; - flags = ''; - while (index < length) { - ch = source[index]; - if (!isIdentifierPart(ch.charCodeAt(0))) { - break; - } - - ++index; - if (ch === '\\' && index < length) { - ch = source[index]; - if (ch === 'u') { - ++index; - restore = index; - ch = scanHexEscape('u'); - if (ch) { - flags += ch; - for (str += '\\u'; restore < index; ++restore) { - str += source[restore]; - } - } else { - index = restore; - flags += 'u'; - str += '\\u'; - } - tolerateUnexpectedToken(); - } else { - str += '\\'; - tolerateUnexpectedToken(); - } - } else { - flags += ch; - str += ch; - } - } - - return { - value: flags, - literal: str - }; - } - - function scanRegExp() { - var start, body, flags, value; - scanning = true; - - lookahead = null; - skipComment(); - start = index; - - body = scanRegExpBody(); - flags = scanRegExpFlags(); - value = testRegExp(body.value, flags.value); - scanning = false; - if (extra.tokenize) { - return { - type: Token.RegularExpression, - value: value, - regex: { - pattern: body.value, - flags: flags.value - }, - lineNumber: lineNumber, - lineStart: lineStart, - start: start, - end: index - }; - } - - return { - literal: body.literal + flags.literal, - value: value, - regex: { - pattern: body.value, - flags: flags.value - }, - start: start, - end: index - }; - } - - function collectRegex() { - var pos, loc, regex, token; - - skipComment(); - - pos = index; - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; - - regex = scanRegExp(); - - loc.end = { - line: lineNumber, - column: index - lineStart - }; - - /* istanbul ignore next */ - if (!extra.tokenize) { - // Pop the previous token, which is likely '/' or '/=' - if (extra.tokens.length > 0) { - token = extra.tokens[extra.tokens.length - 1]; - if (token.range[0] === pos && token.type === 'Punctuator') { - if (token.value === '/' || token.value === '/=') { - extra.tokens.pop(); - } - } - } - - extra.tokens.push({ - type: 'RegularExpression', - value: regex.literal, - regex: regex.regex, - range: [pos, index], - loc: loc - }); - } - - return regex; - } - - function isIdentifierName(token) { - return token.type === Token.Identifier || - token.type === Token.Keyword || - token.type === Token.BooleanLiteral || - token.type === Token.NullLiteral; - } - - function advanceSlash() { - var prevToken, - checkToken; - // Using the following algorithm: - // https://github.com/mozilla/sweet.js/wiki/design - prevToken = extra.tokens[extra.tokens.length - 1]; - if (!prevToken) { - // Nothing before that: it cannot be a division. - return collectRegex(); - } - if (prevToken.type === 'Punctuator') { - if (prevToken.value === ']') { - return scanPunctuator(); - } - if (prevToken.value === ')') { - checkToken = extra.tokens[extra.openParenToken - 1]; - if (checkToken && - checkToken.type === 'Keyword' && - (checkToken.value === 'if' || - checkToken.value === 'while' || - checkToken.value === 'for' || - checkToken.value === 'with')) { - return collectRegex(); - } - return scanPunctuator(); - } - if (prevToken.value === '}') { - // Dividing a function by anything makes little sense, - // but we have to check for that. - if (extra.tokens[extra.openCurlyToken - 3] && - extra.tokens[extra.openCurlyToken - 3].type === 'Keyword') { - // Anonymous function. - checkToken = extra.tokens[extra.openCurlyToken - 4]; - if (!checkToken) { - return scanPunctuator(); - } - } else if (extra.tokens[extra.openCurlyToken - 4] && - extra.tokens[extra.openCurlyToken - 4].type === 'Keyword') { - // Named function. - checkToken = extra.tokens[extra.openCurlyToken - 5]; - if (!checkToken) { - return collectRegex(); - } - } else { - return scanPunctuator(); - } - // checkToken determines whether the function is - // a declaration or an expression. - if (FnExprTokens.indexOf(checkToken.value) >= 0) { - // It is an expression. - return scanPunctuator(); - } - // It is a declaration. - return collectRegex(); - } - return collectRegex(); - } - if (prevToken.type === 'Keyword' && prevToken.value !== 'this') { - return collectRegex(); - } - return scanPunctuator(); - } - - function advance() { - var cp, token; - - if (index >= length) { - return { - type: Token.EOF, - lineNumber: lineNumber, - lineStart: lineStart, - start: index, - end: index - }; - } - - cp = source.charCodeAt(index); - - if (isIdentifierStart(cp)) { - token = scanIdentifier(); - if (strict && isStrictModeReservedWord(token.value)) { - token.type = Token.Keyword; - } - return token; - } - - // Very common: ( and ) and ; - if (cp === 0x28 || cp === 0x29 || cp === 0x3B) { - return scanPunctuator(); - } - - // String literal starts with single quote (U+0027) or double quote (U+0022). - if (cp === 0x27 || cp === 0x22) { - return scanStringLiteral(); - } - - // Dot (.) U+002E can also start a floating-point number, hence the need - // to check the next character. - if (cp === 0x2E) { - if (isDecimalDigit(source.charCodeAt(index + 1))) { - return scanNumericLiteral(); - } - return scanPunctuator(); - } - - if (isDecimalDigit(cp)) { - return scanNumericLiteral(); - } - - // Slash (/) U+002F can also start a regex. - if (extra.tokenize && cp === 0x2F) { - return advanceSlash(); - } - - // Template literals start with ` (U+0060) for template head - // or } (U+007D) for template middle or template tail. - if (cp === 0x60 || (cp === 0x7D && state.curlyStack[state.curlyStack.length - 1] === '${')) { - return scanTemplate(); - } - - // Possible identifier start in a surrogate pair. - if (cp >= 0xD800 && cp < 0xDFFF) { - cp = codePointAt(index); - if (isIdentifierStart(cp)) { - return scanIdentifier(); - } - } - - return scanPunctuator(); - } - - function collectToken() { - var loc, token, value, entry; - - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; - - token = advance(); - loc.end = { - line: lineNumber, - column: index - lineStart - }; - - if (token.type !== Token.EOF) { - value = source.slice(token.start, token.end); - entry = { - type: TokenName[token.type], - value: value, - range: [token.start, token.end], - loc: loc - }; - if (token.regex) { - entry.regex = { - pattern: token.regex.pattern, - flags: token.regex.flags - }; - } - extra.tokens.push(entry); - } - - return token; - } - - function lex() { - var token; - scanning = true; - - lastIndex = index; - lastLineNumber = lineNumber; - lastLineStart = lineStart; - - skipComment(); - - token = lookahead; - - startIndex = index; - startLineNumber = lineNumber; - startLineStart = lineStart; - - lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); - scanning = false; - return token; - } - - function peek() { - scanning = true; - - skipComment(); - - lastIndex = index; - lastLineNumber = lineNumber; - lastLineStart = lineStart; - - startIndex = index; - startLineNumber = lineNumber; - startLineStart = lineStart; - - lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); - scanning = false; - } - - function Position() { - this.line = startLineNumber; - this.column = startIndex - startLineStart; - } - - function SourceLocation() { - this.start = new Position(); - this.end = null; - } - - function WrappingSourceLocation(startToken) { - this.start = { - line: startToken.lineNumber, - column: startToken.start - startToken.lineStart - }; - this.end = null; - } - - function Node() { - if (extra.range) { - this.range = [startIndex, 0]; - } - if (extra.loc) { - this.loc = new SourceLocation(); - } - } - - function WrappingNode(startToken) { - if (extra.range) { - this.range = [startToken.start, 0]; - } - if (extra.loc) { - this.loc = new WrappingSourceLocation(startToken); - } - } - - WrappingNode.prototype = Node.prototype = { - - processComment: function () { - var lastChild, - leadingComments, - trailingComments, - bottomRight = extra.bottomRightStack, - i, - comment, - last = bottomRight[bottomRight.length - 1]; - - if (this.type === Syntax.Program) { - if (this.body.length > 0) { - return; - } - } - - if (extra.trailingComments.length > 0) { - trailingComments = []; - for (i = extra.trailingComments.length - 1; i >= 0; --i) { - comment = extra.trailingComments[i]; - if (comment.range[0] >= this.range[1]) { - trailingComments.unshift(comment); - extra.trailingComments.splice(i, 1); - } - } - extra.trailingComments = []; - } else { - if (last && last.trailingComments && last.trailingComments[0].range[0] >= this.range[1]) { - trailingComments = last.trailingComments; - delete last.trailingComments; - } - } - - // Eating the stack. - while (last && last.range[0] >= this.range[0]) { - lastChild = bottomRight.pop(); - last = bottomRight[bottomRight.length - 1]; - } - - if (lastChild) { - if (lastChild.leadingComments) { - leadingComments = []; - for (i = lastChild.leadingComments.length - 1; i >= 0; --i) { - comment = lastChild.leadingComments[i]; - if (comment.range[1] <= this.range[0]) { - leadingComments.unshift(comment); - lastChild.leadingComments.splice(i, 1); - } - } - - if (!lastChild.leadingComments.length) { - lastChild.leadingComments = undefined; - } - } - } else if (extra.leadingComments.length > 0) { - leadingComments = []; - for (i = extra.leadingComments.length - 1; i >= 0; --i) { - comment = extra.leadingComments[i]; - if (comment.range[1] <= this.range[0]) { - leadingComments.unshift(comment); - extra.leadingComments.splice(i, 1); - } - } - } - - - if (leadingComments && leadingComments.length > 0) { - this.leadingComments = leadingComments; - } - if (trailingComments && trailingComments.length > 0) { - this.trailingComments = trailingComments; - } - - bottomRight.push(this); - }, - - finish: function () { - if (extra.range) { - this.range[1] = lastIndex; - } - if (extra.loc) { - this.loc.end = { - line: lastLineNumber, - column: lastIndex - lastLineStart - }; - if (extra.source) { - this.loc.source = extra.source; - } - } - - if (extra.attachComment) { - this.processComment(); - } - }, - - finishArrayExpression: function (elements) { - this.type = Syntax.ArrayExpression; - this.elements = elements; - this.finish(); - return this; - }, - - finishArrayPattern: function (elements) { - this.type = Syntax.ArrayPattern; - this.elements = elements; - this.finish(); - return this; - }, - - finishArrowFunctionExpression: function (params, defaults, body, expression) { - this.type = Syntax.ArrowFunctionExpression; - this.id = null; - this.params = params; - this.defaults = defaults; - this.body = body; - this.generator = false; - this.expression = expression; - this.finish(); - return this; - }, - - finishAssignmentExpression: function (operator, left, right) { - this.type = Syntax.AssignmentExpression; - this.operator = operator; - this.left = left; - this.right = right; - this.finish(); - return this; - }, - - finishAssignmentPattern: function (left, right) { - this.type = Syntax.AssignmentPattern; - this.left = left; - this.right = right; - this.finish(); - return this; - }, - - finishBinaryExpression: function (operator, left, right) { - this.type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression : Syntax.BinaryExpression; - this.operator = operator; - this.left = left; - this.right = right; - this.finish(); - return this; - }, - - finishBlockStatement: function (body) { - this.type = Syntax.BlockStatement; - this.body = body; - this.finish(); - return this; - }, - - finishBreakStatement: function (label) { - this.type = Syntax.BreakStatement; - this.label = label; - this.finish(); - return this; - }, - - finishCallExpression: function (callee, args) { - this.type = Syntax.CallExpression; - this.callee = callee; - this.arguments = args; - this.finish(); - return this; - }, - - finishCatchClause: function (param, body) { - this.type = Syntax.CatchClause; - this.param = param; - this.body = body; - this.finish(); - return this; - }, - - finishClassBody: function (body) { - this.type = Syntax.ClassBody; - this.body = body; - this.finish(); - return this; - }, - - finishClassDeclaration: function (id, superClass, body) { - this.type = Syntax.ClassDeclaration; - this.id = id; - this.superClass = superClass; - this.body = body; - this.finish(); - return this; - }, - - finishClassExpression: function (id, superClass, body) { - this.type = Syntax.ClassExpression; - this.id = id; - this.superClass = superClass; - this.body = body; - this.finish(); - return this; - }, - - finishConditionalExpression: function (test, consequent, alternate) { - this.type = Syntax.ConditionalExpression; - this.test = test; - this.consequent = consequent; - this.alternate = alternate; - this.finish(); - return this; - }, - - finishContinueStatement: function (label) { - this.type = Syntax.ContinueStatement; - this.label = label; - this.finish(); - return this; - }, - - finishDebuggerStatement: function () { - this.type = Syntax.DebuggerStatement; - this.finish(); - return this; - }, - - finishDoWhileStatement: function (body, test) { - this.type = Syntax.DoWhileStatement; - this.body = body; - this.test = test; - this.finish(); - return this; - }, - - finishEmptyStatement: function () { - this.type = Syntax.EmptyStatement; - this.finish(); - return this; - }, - - finishExpressionStatement: function (expression) { - this.type = Syntax.ExpressionStatement; - this.expression = expression; - this.finish(); - return this; - }, - - finishForStatement: function (init, test, update, body) { - this.type = Syntax.ForStatement; - this.init = init; - this.test = test; - this.update = update; - this.body = body; - this.finish(); - return this; - }, - - finishForOfStatement: function (left, right, body) { - this.type = Syntax.ForOfStatement; - this.left = left; - this.right = right; - this.body = body; - this.finish(); - return this; - }, - - finishForInStatement: function (left, right, body) { - this.type = Syntax.ForInStatement; - this.left = left; - this.right = right; - this.body = body; - this.each = false; - this.finish(); - return this; - }, - - finishFunctionDeclaration: function (id, params, defaults, body, generator) { - this.type = Syntax.FunctionDeclaration; - this.id = id; - this.params = params; - this.defaults = defaults; - this.body = body; - this.generator = generator; - this.expression = false; - this.finish(); - return this; - }, - - finishFunctionExpression: function (id, params, defaults, body, generator) { - this.type = Syntax.FunctionExpression; - this.id = id; - this.params = params; - this.defaults = defaults; - this.body = body; - this.generator = generator; - this.expression = false; - this.finish(); - return this; - }, - - finishIdentifier: function (name) { - this.type = Syntax.Identifier; - this.name = name; - this.finish(); - return this; - }, - - finishIfStatement: function (test, consequent, alternate) { - this.type = Syntax.IfStatement; - this.test = test; - this.consequent = consequent; - this.alternate = alternate; - this.finish(); - return this; - }, - - finishLabeledStatement: function (label, body) { - this.type = Syntax.LabeledStatement; - this.label = label; - this.body = body; - this.finish(); - return this; - }, - - finishLiteral: function (token) { - this.type = Syntax.Literal; - this.value = token.value; - this.raw = source.slice(token.start, token.end); - if (token.regex) { - this.regex = token.regex; - } - this.finish(); - return this; - }, - - finishMemberExpression: function (accessor, object, property) { - this.type = Syntax.MemberExpression; - this.computed = accessor === '['; - this.object = object; - this.property = property; - this.finish(); - return this; - }, - - finishMetaProperty: function (meta, property) { - this.type = Syntax.MetaProperty; - this.meta = meta; - this.property = property; - this.finish(); - return this; - }, - - finishNewExpression: function (callee, args) { - this.type = Syntax.NewExpression; - this.callee = callee; - this.arguments = args; - this.finish(); - return this; - }, - - finishObjectExpression: function (properties) { - this.type = Syntax.ObjectExpression; - this.properties = properties; - this.finish(); - return this; - }, - - finishObjectPattern: function (properties) { - this.type = Syntax.ObjectPattern; - this.properties = properties; - this.finish(); - return this; - }, - - finishPostfixExpression: function (operator, argument) { - this.type = Syntax.UpdateExpression; - this.operator = operator; - this.argument = argument; - this.prefix = false; - this.finish(); - return this; - }, - - finishProgram: function (body, sourceType) { - this.type = Syntax.Program; - this.body = body; - this.sourceType = sourceType; - this.finish(); - return this; - }, - - finishProperty: function (kind, key, computed, value, method, shorthand) { - this.type = Syntax.Property; - this.key = key; - this.computed = computed; - this.value = value; - this.kind = kind; - this.method = method; - this.shorthand = shorthand; - this.finish(); - return this; - }, - - finishRestElement: function (argument) { - this.type = Syntax.RestElement; - this.argument = argument; - this.finish(); - return this; - }, - - finishReturnStatement: function (argument) { - this.type = Syntax.ReturnStatement; - this.argument = argument; - this.finish(); - return this; - }, - - finishSequenceExpression: function (expressions) { - this.type = Syntax.SequenceExpression; - this.expressions = expressions; - this.finish(); - return this; - }, - - finishSpreadElement: function (argument) { - this.type = Syntax.SpreadElement; - this.argument = argument; - this.finish(); - return this; - }, - - finishSwitchCase: function (test, consequent) { - this.type = Syntax.SwitchCase; - this.test = test; - this.consequent = consequent; - this.finish(); - return this; - }, - - finishSuper: function () { - this.type = Syntax.Super; - this.finish(); - return this; - }, - - finishSwitchStatement: function (discriminant, cases) { - this.type = Syntax.SwitchStatement; - this.discriminant = discriminant; - this.cases = cases; - this.finish(); - return this; - }, - - finishTaggedTemplateExpression: function (tag, quasi) { - this.type = Syntax.TaggedTemplateExpression; - this.tag = tag; - this.quasi = quasi; - this.finish(); - return this; - }, - - finishTemplateElement: function (value, tail) { - this.type = Syntax.TemplateElement; - this.value = value; - this.tail = tail; - this.finish(); - return this; - }, - - finishTemplateLiteral: function (quasis, expressions) { - this.type = Syntax.TemplateLiteral; - this.quasis = quasis; - this.expressions = expressions; - this.finish(); - return this; - }, - - finishThisExpression: function () { - this.type = Syntax.ThisExpression; - this.finish(); - return this; - }, - - finishThrowStatement: function (argument) { - this.type = Syntax.ThrowStatement; - this.argument = argument; - this.finish(); - return this; - }, - - finishTryStatement: function (block, handler, finalizer) { - this.type = Syntax.TryStatement; - this.block = block; - this.guardedHandlers = []; - this.handlers = handler ? [handler] : []; - this.handler = handler; - this.finalizer = finalizer; - this.finish(); - return this; - }, - - finishUnaryExpression: function (operator, argument) { - this.type = (operator === '++' || operator === '--') ? Syntax.UpdateExpression : Syntax.UnaryExpression; - this.operator = operator; - this.argument = argument; - this.prefix = true; - this.finish(); - return this; - }, - - finishVariableDeclaration: function (declarations) { - this.type = Syntax.VariableDeclaration; - this.declarations = declarations; - this.kind = 'var'; - this.finish(); - return this; - }, - - finishLexicalDeclaration: function (declarations, kind) { - this.type = Syntax.VariableDeclaration; - this.declarations = declarations; - this.kind = kind; - this.finish(); - return this; - }, - - finishVariableDeclarator: function (id, init) { - this.type = Syntax.VariableDeclarator; - this.id = id; - this.init = init; - this.finish(); - return this; - }, - - finishWhileStatement: function (test, body) { - this.type = Syntax.WhileStatement; - this.test = test; - this.body = body; - this.finish(); - return this; - }, - - finishWithStatement: function (object, body) { - this.type = Syntax.WithStatement; - this.object = object; - this.body = body; - this.finish(); - return this; - }, - - finishExportSpecifier: function (local, exported) { - this.type = Syntax.ExportSpecifier; - this.exported = exported || local; - this.local = local; - this.finish(); - return this; - }, - - finishImportDefaultSpecifier: function (local) { - this.type = Syntax.ImportDefaultSpecifier; - this.local = local; - this.finish(); - return this; - }, - - finishImportNamespaceSpecifier: function (local) { - this.type = Syntax.ImportNamespaceSpecifier; - this.local = local; - this.finish(); - return this; - }, - - finishExportNamedDeclaration: function (declaration, specifiers, src) { - this.type = Syntax.ExportNamedDeclaration; - this.declaration = declaration; - this.specifiers = specifiers; - this.source = src; - this.finish(); - return this; - }, - - finishExportDefaultDeclaration: function (declaration) { - this.type = Syntax.ExportDefaultDeclaration; - this.declaration = declaration; - this.finish(); - return this; - }, - - finishExportAllDeclaration: function (src) { - this.type = Syntax.ExportAllDeclaration; - this.source = src; - this.finish(); - return this; - }, - - finishImportSpecifier: function (local, imported) { - this.type = Syntax.ImportSpecifier; - this.local = local || imported; - this.imported = imported; - this.finish(); - return this; - }, - - finishImportDeclaration: function (specifiers, src) { - this.type = Syntax.ImportDeclaration; - this.specifiers = specifiers; - this.source = src; - this.finish(); - return this; - }, - - finishYieldExpression: function (argument, delegate) { - this.type = Syntax.YieldExpression; - this.argument = argument; - this.delegate = delegate; - this.finish(); - return this; - } - }; - - - function recordError(error) { - var e, existing; - - for (e = 0; e < extra.errors.length; e++) { - existing = extra.errors[e]; - // Prevent duplicated error. - /* istanbul ignore next */ - if (existing.index === error.index && existing.message === error.message) { - return; - } - } - - extra.errors.push(error); - } - - function constructError(msg, column) { - var error = new Error(msg); - try { - throw error; - } catch (base) { - /* istanbul ignore else */ - if (Object.create && Object.defineProperty) { - error = Object.create(base); - Object.defineProperty(error, 'column', { value: column }); - } - } finally { - return error; - } - } - - function createError(line, pos, description) { - var msg, column, error; - - msg = 'Line ' + line + ': ' + description; - column = pos - (scanning ? lineStart : lastLineStart) + 1; - error = constructError(msg, column); - error.lineNumber = line; - error.description = description; - error.index = pos; - return error; - } - - // Throw an exception - - function throwError(messageFormat) { - var args, msg; - - args = Array.prototype.slice.call(arguments, 1); - msg = messageFormat.replace(/%(\d)/g, - function (whole, idx) { - assert(idx < args.length, 'Message reference must be in range'); - return args[idx]; - } - ); - - throw createError(lastLineNumber, lastIndex, msg); - } - - function tolerateError(messageFormat) { - var args, msg, error; - log(JSON.stringify(arguments)) - args = Array.prototype.slice.call(arguments, 1); - log('here') - log(args) - /* istanbul ignore next */ - msg = messageFormat.replace(/%(\d)/g, - function (whole, idx) { - assert(idx < args.length, 'Message reference must be in range'); - return args[idx]; - } - ); - - error = createError(lineNumber, lastIndex, msg); - if (extra.errors) { - recordError(error); - } else { - throw error; - } - } - - // Throw an exception because of the token. - - function unexpectedTokenError(token, message) { - var value, msg = message || Messages.UnexpectedToken; - - if (token) { - if (!message) { - msg = (token.type === Token.EOF) ? Messages.UnexpectedEOS : - (token.type === Token.Identifier) ? Messages.UnexpectedIdentifier : - (token.type === Token.NumericLiteral) ? Messages.UnexpectedNumber : - (token.type === Token.StringLiteral) ? Messages.UnexpectedString : - (token.type === Token.Template) ? Messages.UnexpectedTemplate : - Messages.UnexpectedToken; - - if (token.type === Token.Keyword) { - if (isFutureReservedWord(token.value)) { - msg = Messages.UnexpectedReserved; - } else if (strict && isStrictModeReservedWord(token.value)) { - msg = Messages.StrictReservedWord; - } - } - } - - value = (token.type === Token.Template) ? token.value.raw : token.value; - } else { - value = 'ILLEGAL'; - } - - msg = msg.replace('%0', value); - - return (token && typeof token.lineNumber === 'number') ? - createError(token.lineNumber, token.start, msg) : - createError(scanning ? lineNumber : lastLineNumber, scanning ? index : lastIndex, msg); - } - - function throwUnexpectedToken(token, message) { - throw unexpectedTokenError(token, message); - } - - function tolerateUnexpectedToken(token, message) { - var error = unexpectedTokenError(token, message); - if (extra.errors) { - recordError(error); - } else { - throw error; - } - } - - // Expect the next token to match the specified punctuator. - // If not, an exception will be thrown. - - function expect(value) { - var token = lex(); - if (token.type !== Token.Punctuator || token.value !== value) { - throwUnexpectedToken(token); - } - } - - /** - * @name expectCommaSeparator - * @description Quietly expect a comma when in tolerant mode, otherwise delegates - * to expect(value) - * @since 2.0 - */ - function expectCommaSeparator() { - var token; - - if (extra.errors) { - token = lookahead; - if (token.type === Token.Punctuator && token.value === ',') { - lex(); - } else if (token.type === Token.Punctuator && token.value === ';') { - lex(); - tolerateUnexpectedToken(token); - } else { - tolerateUnexpectedToken(token, Messages.UnexpectedToken); - } - } else { - expect(','); - } - } - - // Expect the next token to match the specified keyword. - // If not, an exception will be thrown. - - function expectKeyword(keyword) { - var token = lex(); - if (token.type !== Token.Keyword || token.value !== keyword) { - throwUnexpectedToken(token); - } - } - - // Return true if the next token matches the specified punctuator. - - function match(value) { - return lookahead.type === Token.Punctuator && lookahead.value === value; - } - - // Return true if the next token matches the specified keyword - - function matchKeyword(keyword) { - return lookahead.type === Token.Keyword && lookahead.value === keyword; - } - - // Return true if the next token matches the specified contextual keyword - // (where an identifier is sometimes a keyword depending on the context) - - function matchContextualKeyword(keyword) { - return lookahead.type === Token.Identifier && lookahead.value === keyword; - } - - // Return true if the next token is an assignment operator - - function matchAssign() { - var op; - - if (lookahead.type !== Token.Punctuator) { - return false; - } - op = lookahead.value; - return op === '=' || - op === '*=' || - op === '/=' || - op === '%=' || - op === '+=' || - op === '-=' || - op === '<<=' || - op === '>>=' || - op === '>>>=' || - op === '&=' || - op === '^=' || - op === '|='; - } - - function consumeSemicolon() { - // Catch the very common case first: immediately a semicolon (U+003B). - if (source.charCodeAt(startIndex) === 0x3B || match(';')) { - lex(); - return; - } - - if (hasLineTerminator) { - return; - } - - // FIXME(ikarienator): this is seemingly an issue in the previous location info convention. - lastIndex = startIndex; - lastLineNumber = startLineNumber; - lastLineStart = startLineStart; - - if (lookahead.type !== Token.EOF && !match('}')) { - throwUnexpectedToken(lookahead); - } - } - - // Cover grammar support. - // - // When an assignment expression position starts with an left parenthesis, the determination of the type - // of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead) - // or the first comma. This situation also defers the determination of all the expressions nested in the pair. - // - // There are three productions that can be parsed in a parentheses pair that needs to be determined - // after the outermost pair is closed. They are: - // - // 1. AssignmentExpression - // 2. BindingElements - // 3. AssignmentTargets - // - // In order to avoid exponential backtracking, we use two flags to denote if the production can be - // binding element or assignment target. - // - // The three productions have the relationship: - // - // BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression - // - // with a single exception that CoverInitializedName when used directly in an Expression, generates - // an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the - // first usage of CoverInitializedName and report it when we reached the end of the parentheses pair. - // - // isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not - // effect the current flags. This means the production the parser parses is only used as an expression. Therefore - // the CoverInitializedName check is conducted. - // - // inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates - // the flags outside of the parser. This means the production the parser parses is used as a part of a potential - // pattern. The CoverInitializedName check is deferred. - function isolateCoverGrammar(parser) { - var oldIsBindingElement = isBindingElement, - oldIsAssignmentTarget = isAssignmentTarget, - oldFirstCoverInitializedNameError = firstCoverInitializedNameError, - result; - isBindingElement = true; - isAssignmentTarget = true; - firstCoverInitializedNameError = null; - result = parser(); - if (firstCoverInitializedNameError !== null) { - throwUnexpectedToken(firstCoverInitializedNameError); - } - isBindingElement = oldIsBindingElement; - isAssignmentTarget = oldIsAssignmentTarget; - firstCoverInitializedNameError = oldFirstCoverInitializedNameError; - return result; - } - - function inheritCoverGrammar(parser) { - var oldIsBindingElement = isBindingElement, - oldIsAssignmentTarget = isAssignmentTarget, - oldFirstCoverInitializedNameError = firstCoverInitializedNameError, - result; - isBindingElement = true; - isAssignmentTarget = true; - firstCoverInitializedNameError = null; - result = parser(); - isBindingElement = isBindingElement && oldIsBindingElement; - isAssignmentTarget = isAssignmentTarget && oldIsAssignmentTarget; - firstCoverInitializedNameError = oldFirstCoverInitializedNameError || firstCoverInitializedNameError; - return result; - } - - // ECMA-262 13.3.3 Destructuring Binding Patterns - - function parseArrayPattern(params, kind) { - var node = new Node(), elements = [], rest, restNode; - expect('['); - - while (!match(']')) { - if (match(',')) { - lex(); - elements.push(null); - } else { - if (match('...')) { - restNode = new Node(); - lex(); - params.push(lookahead); - rest = parseVariableIdentifier(params, kind); - elements.push(restNode.finishRestElement(rest)); - break; - } else { - elements.push(parsePatternWithDefault(params, kind)); - } - if (!match(']')) { - expect(','); - } - } - - } - - expect(']'); - - return node.finishArrayPattern(elements); - } - - function parsePropertyPattern(params, kind) { - var node = new Node(), key, keyToken, computed = match('['), init; - if (lookahead.type === Token.Identifier) { - keyToken = lookahead; - key = parseVariableIdentifier(); - if (match('=')) { - params.push(keyToken); - lex(); - init = parseAssignmentExpression(); - - return node.finishProperty( - 'init', key, false, - new WrappingNode(keyToken).finishAssignmentPattern(key, init), false, false); - } else if (!match(':')) { - params.push(keyToken); - return node.finishProperty('init', key, false, key, false, true); - } - } else { - key = parseObjectPropertyKey(params, kind); - } - expect(':'); - init = parsePatternWithDefault(params, kind); - return node.finishProperty('init', key, computed, init, false, false); - } - - function parseObjectPattern(params, kind) { - var node = new Node(), properties = []; - - expect('{'); - - while (!match('}')) { - properties.push(parsePropertyPattern(params, kind)); - if (!match('}')) { - expect(','); - } - } - - lex(); - - return node.finishObjectPattern(properties); - } - - function parsePattern(params, kind) { - if (match('[')) { - return parseArrayPattern(params, kind); - } else if (match('{')) { - return parseObjectPattern(params, kind); - } - params.push(lookahead); - return parseVariableIdentifier(kind); - } - - function parsePatternWithDefault(params, kind) { - var startToken = lookahead, pattern, previousAllowYield, right; - pattern = parsePattern(params, kind); - if (match('=')) { - lex(); - previousAllowYield = state.allowYield; - state.allowYield = true; - right = isolateCoverGrammar(parseAssignmentExpression); - state.allowYield = previousAllowYield; - pattern = new WrappingNode(startToken).finishAssignmentPattern(pattern, right); - } - return pattern; - } - - // ECMA-262 12.2.5 Array Initializer - - function parseArrayInitializer() { - var elements = [], node = new Node(), restSpread; - - expect('['); - - while (!match(']')) { - if (match(',')) { - lex(); - elements.push(null); - } else if (match('...')) { - restSpread = new Node(); - lex(); - restSpread.finishSpreadElement(inheritCoverGrammar(parseAssignmentExpression)); - - if (!match(']')) { - isAssignmentTarget = isBindingElement = false; - expect(','); - } - elements.push(restSpread); - } else { - elements.push(inheritCoverGrammar(parseAssignmentExpression)); - - if (!match(']')) { - expect(','); - } - } - } - - lex(); - - return node.finishArrayExpression(elements); - } - - // ECMA-262 12.2.6 Object Initializer - - function parsePropertyFunction(node, paramInfo, isGenerator) { - var previousStrict, body; - - isAssignmentTarget = isBindingElement = false; - - previousStrict = strict; - body = isolateCoverGrammar(parseFunctionSourceElements); - - if (strict && paramInfo.firstRestricted) { - tolerateUnexpectedToken(paramInfo.firstRestricted, paramInfo.message); - } - if (strict && paramInfo.stricted) { - tolerateUnexpectedToken(paramInfo.stricted, paramInfo.message); - } - - strict = previousStrict; - return node.finishFunctionExpression(null, paramInfo.params, paramInfo.defaults, body, isGenerator); - } - - function parsePropertyMethodFunction() { - var params, method, node = new Node(), - previousAllowYield = state.allowYield; - - state.allowYield = false; - params = parseParams(); - state.allowYield = previousAllowYield; - - state.allowYield = false; - method = parsePropertyFunction(node, params, false); - state.allowYield = previousAllowYield; - - return method; - } - - function parseObjectPropertyKey() { - var token, node = new Node(), expr; - - token = lex(); - - // Note: This function is called only from parseObjectProperty(), where - // EOF and Punctuator tokens are already filtered out. - - switch (token.type) { - case Token.StringLiteral: - case Token.NumericLiteral: - if (strict && token.octal) { - tolerateUnexpectedToken(token, Messages.StrictOctalLiteral); - } - return node.finishLiteral(token); - case Token.Identifier: - case Token.BooleanLiteral: - case Token.NullLiteral: - case Token.Keyword: - return node.finishIdentifier(token.value); - case Token.Punctuator: - if (token.value === '[') { - expr = isolateCoverGrammar(parseAssignmentExpression); - expect(']'); - return expr; - } - break; - } - throwUnexpectedToken(token); - } - - function lookaheadPropertyName() { - switch (lookahead.type) { - case Token.Identifier: - case Token.StringLiteral: - case Token.BooleanLiteral: - case Token.NullLiteral: - case Token.NumericLiteral: - case Token.Keyword: - return true; - case Token.Punctuator: - return lookahead.value === '['; - } - return false; - } - - // This function is to try to parse a MethodDefinition as defined in 14.3. But in the case of object literals, - // it might be called at a position where there is in fact a short hand identifier pattern or a data property. - // This can only be determined after we consumed up to the left parentheses. - // - // In order to avoid back tracking, it returns `null` if the position is not a MethodDefinition and the caller - // is responsible to visit other options. - function tryParseMethodDefinition(token, key, computed, node) { - var value, options, methodNode, params, - previousAllowYield = state.allowYield; - - if (token.type === Token.Identifier) { - // check for `get` and `set`; - - if (token.value === 'get' && lookaheadPropertyName()) { - computed = match('['); - key = parseObjectPropertyKey(); - methodNode = new Node(); - expect('('); - expect(')'); - - state.allowYield = false; - value = parsePropertyFunction(methodNode, { - params: [], - defaults: [], - stricted: null, - firstRestricted: null, - message: null - }, false); - state.allowYield = previousAllowYield; - - return node.finishProperty('get', key, computed, value, false, false); - } else if (token.value === 'set' && lookaheadPropertyName()) { - computed = match('['); - key = parseObjectPropertyKey(); - methodNode = new Node(); - expect('('); - - options = { - params: [], - defaultCount: 0, - defaults: [], - firstRestricted: null, - paramSet: {} - }; - if (match(')')) { - tolerateUnexpectedToken(lookahead); - } else { - state.allowYield = false; - parseParam(options); - state.allowYield = previousAllowYield; - if (options.defaultCount === 0) { - options.defaults = []; - } - } - expect(')'); - - state.allowYield = false; - value = parsePropertyFunction(methodNode, options, false); - state.allowYield = previousAllowYield; - - return node.finishProperty('set', key, computed, value, false, false); - } - } else if (token.type === Token.Punctuator && token.value === '*' && lookaheadPropertyName()) { - computed = match('['); - key = parseObjectPropertyKey(); - methodNode = new Node(); - - state.allowYield = true; - params = parseParams(); - state.allowYield = previousAllowYield; - - state.allowYield = false; - value = parsePropertyFunction(methodNode, params, true); - state.allowYield = previousAllowYield; - - return node.finishProperty('init', key, computed, value, true, false); - } - - if (key && match('(')) { - value = parsePropertyMethodFunction(); - return node.finishProperty('init', key, computed, value, true, false); - } - - // Not a MethodDefinition. - return null; - } - - function parseObjectProperty(hasProto) { - var token = lookahead, node = new Node(), computed, key, maybeMethod, proto, value; - - computed = match('['); - if (match('*')) { - lex(); - } else { - key = parseObjectPropertyKey(); - } - maybeMethod = tryParseMethodDefinition(token, key, computed, node); - if (maybeMethod) { - return maybeMethod; - } - - if (!key) { - throwUnexpectedToken(lookahead); - } - - // Check for duplicated __proto__ - if (!computed) { - proto = (key.type === Syntax.Identifier && key.name === '__proto__') || - (key.type === Syntax.Literal && key.value === '__proto__'); - if (hasProto.value && proto) { - tolerateError(Messages.DuplicateProtoProperty); - } - hasProto.value |= proto; - } - - if (match(':')) { - lex(); - value = inheritCoverGrammar(parseAssignmentExpression); - return node.finishProperty('init', key, computed, value, false, false); - } - - if (token.type === Token.Identifier) { - if (match('=')) { - firstCoverInitializedNameError = lookahead; - lex(); - value = isolateCoverGrammar(parseAssignmentExpression); - return node.finishProperty('init', key, computed, - new WrappingNode(token).finishAssignmentPattern(key, value), false, true); - } - return node.finishProperty('init', key, computed, key, false, true); - } - - throwUnexpectedToken(lookahead); - } - - function parseObjectInitializer() { - var properties = [], hasProto = {value: false}, node = new Node(); - - expect('{'); - - while (!match('}')) { - properties.push(parseObjectProperty(hasProto)); - - if (!match('}')) { - expectCommaSeparator(); - } - } - - expect('}'); - - return node.finishObjectExpression(properties); - } - - function reinterpretExpressionAsPattern(expr) { - var i; - switch (expr.type) { - case Syntax.Identifier: - case Syntax.MemberExpression: - case Syntax.RestElement: - case Syntax.AssignmentPattern: - break; - case Syntax.SpreadElement: - expr.type = Syntax.RestElement; - reinterpretExpressionAsPattern(expr.argument); - break; - case Syntax.ArrayExpression: - expr.type = Syntax.ArrayPattern; - for (i = 0; i < expr.elements.length; i++) { - if (expr.elements[i] !== null) { - reinterpretExpressionAsPattern(expr.elements[i]); - } - } - break; - case Syntax.ObjectExpression: - expr.type = Syntax.ObjectPattern; - for (i = 0; i < expr.properties.length; i++) { - reinterpretExpressionAsPattern(expr.properties[i].value); - } - break; - case Syntax.AssignmentExpression: - expr.type = Syntax.AssignmentPattern; - reinterpretExpressionAsPattern(expr.left); - break; - default: - // Allow other node type for tolerant parsing. - break; - } - } - - // ECMA-262 12.2.9 Template Literals - - function parseTemplateElement(option) { - var node, token; - - if (lookahead.type !== Token.Template || (option.head && !lookahead.head)) { - throwUnexpectedToken(); - } - - node = new Node(); - token = lex(); - - return node.finishTemplateElement({ raw: token.value.raw, cooked: token.value.cooked }, token.tail); - } - - function parseTemplateLiteral() { - var quasi, quasis, expressions, node = new Node(); - - quasi = parseTemplateElement({ head: true }); - quasis = [quasi]; - expressions = []; - - while (!quasi.tail) { - expressions.push(parseExpression()); - quasi = parseTemplateElement({ head: false }); - quasis.push(quasi); - } - - return node.finishTemplateLiteral(quasis, expressions); - } - - // ECMA-262 12.2.10 The Grouping Operator - - function parseGroupExpression() { - var expr, expressions, startToken, i, params = []; - - expect('('); - - if (match(')')) { - lex(); - if (!match('=>')) { - expect('=>'); - } - return { - type: PlaceHolders.ArrowParameterPlaceHolder, - params: [], - rawParams: [] - }; - } - - startToken = lookahead; - if (match('...')) { - expr = parseRestElement(params); - expect(')'); - if (!match('=>')) { - expect('=>'); - } - return { - type: PlaceHolders.ArrowParameterPlaceHolder, - params: [expr] - }; - } - - isBindingElement = true; - expr = inheritCoverGrammar(parseAssignmentExpression); - - if (match(',')) { - isAssignmentTarget = false; - expressions = [expr]; - - while (startIndex < length) { - if (!match(',')) { - break; - } - lex(); - - if (match('...')) { - if (!isBindingElement) { - throwUnexpectedToken(lookahead); - } - expressions.push(parseRestElement(params)); - expect(')'); - if (!match('=>')) { - expect('=>'); - } - isBindingElement = false; - for (i = 0; i < expressions.length; i++) { - reinterpretExpressionAsPattern(expressions[i]); - } - return { - type: PlaceHolders.ArrowParameterPlaceHolder, - params: expressions - }; - } - - expressions.push(inheritCoverGrammar(parseAssignmentExpression)); - } - - expr = new WrappingNode(startToken).finishSequenceExpression(expressions); - } - - - expect(')'); - - if (match('=>')) { - if (expr.type === Syntax.Identifier && expr.name === 'yield') { - return { - type: PlaceHolders.ArrowParameterPlaceHolder, - params: [expr] - }; - } - - if (!isBindingElement) { - throwUnexpectedToken(lookahead); - } - - if (expr.type === Syntax.SequenceExpression) { - for (i = 0; i < expr.expressions.length; i++) { - reinterpretExpressionAsPattern(expr.expressions[i]); - } - } else { - reinterpretExpressionAsPattern(expr); - } - - expr = { - type: PlaceHolders.ArrowParameterPlaceHolder, - params: expr.type === Syntax.SequenceExpression ? expr.expressions : [expr] - }; - } - isBindingElement = false; - return expr; - } - - - // ECMA-262 12.2 Primary Expressions - - function parsePrimaryExpression() { - var type, token, expr, node; - - if (match('(')) { - isBindingElement = false; - return inheritCoverGrammar(parseGroupExpression); - } - - if (match('[')) { - return inheritCoverGrammar(parseArrayInitializer); - } - - if (match('{')) { - return inheritCoverGrammar(parseObjectInitializer); - } - - type = lookahead.type; - node = new Node(); - - if (type === Token.Identifier) { - if (state.sourceType === 'module' && lookahead.value === 'await') { - tolerateUnexpectedToken(lookahead); - } - expr = node.finishIdentifier(lex().value); - } else if (type === Token.StringLiteral || type === Token.NumericLiteral) { - isAssignmentTarget = isBindingElement = false; - if (strict && lookahead.octal) { - tolerateUnexpectedToken(lookahead, Messages.StrictOctalLiteral); - } - expr = node.finishLiteral(lex()); - } else if (type === Token.Keyword) { - if (!strict && state.allowYield && matchKeyword('yield')) { - return parseNonComputedProperty(); - } - isAssignmentTarget = isBindingElement = false; - if (matchKeyword('function')) { - return parseFunctionExpression(); - } - if (matchKeyword('this')) { - lex(); - return node.finishThisExpression(); - } - if (matchKeyword('class')) { - return parseClassExpression(); - } - throwUnexpectedToken(lex()); - } else if (type === Token.BooleanLiteral) { - isAssignmentTarget = isBindingElement = false; - token = lex(); - token.value = (token.value === 'true'); - expr = node.finishLiteral(token); - } else if (type === Token.NullLiteral) { - isAssignmentTarget = isBindingElement = false; - token = lex(); - token.value = null; - expr = node.finishLiteral(token); - } else if (match('/') || match('/=')) { - isAssignmentTarget = isBindingElement = false; - index = startIndex; - - if (typeof extra.tokens !== 'undefined') { - token = collectRegex(); - } else { - token = scanRegExp(); - } - lex(); - expr = node.finishLiteral(token); - } else if (type === Token.Template) { - expr = parseTemplateLiteral(); - } else { - throwUnexpectedToken(lex()); - } - - return expr; - } - - // ECMA-262 12.3 Left-Hand-Side Expressions - - function parseArguments() { - var args = [], expr; - - expect('('); - - if (!match(')')) { - while (startIndex < length) { - if (match('...')) { - expr = new Node(); - lex(); - expr.finishSpreadElement(isolateCoverGrammar(parseAssignmentExpression)); - } else { - expr = isolateCoverGrammar(parseAssignmentExpression); - } - args.push(expr); - if (match(')')) { - break; - } - expectCommaSeparator(); - } - } - - expect(')'); - - return args; - } - - function parseNonComputedProperty() { - var token, node = new Node(); - - token = lex(); - - if (!isIdentifierName(token)) { - throwUnexpectedToken(token); - } - - return node.finishIdentifier(token.value); - } - - function parseNonComputedMember() { - expect('.'); - - return parseNonComputedProperty(); - } - - function parseComputedMember() { - var expr; - - expect('['); - - expr = isolateCoverGrammar(parseExpression); - - expect(']'); - - return expr; - } - - // ECMA-262 12.3.3 The new Operator - - function parseNewExpression() { - var callee, args, node = new Node(); - - expectKeyword('new'); - - if (match('.')) { - lex(); - if (lookahead.type === Token.Identifier && lookahead.value === 'target') { - if (state.inFunctionBody) { - lex(); - return node.finishMetaProperty('new', 'target'); - } - } - throwUnexpectedToken(lookahead); - } - - callee = isolateCoverGrammar(parseLeftHandSideExpression); - args = match('(') ? parseArguments() : []; - - isAssignmentTarget = isBindingElement = false; - - return node.finishNewExpression(callee, args); - } - - // ECMA-262 12.3.4 Function Calls - - function parseLeftHandSideExpressionAllowCall() { - var quasi, expr, args, property, startToken, previousAllowIn = state.allowIn; - - startToken = lookahead; - state.allowIn = true; - - if (matchKeyword('super') && state.inFunctionBody) { - expr = new Node(); - lex(); - expr = expr.finishSuper(); - if (!match('(') && !match('.') && !match('[')) { - throwUnexpectedToken(lookahead); - } - } else { - expr = inheritCoverGrammar(matchKeyword('new') ? parseNewExpression : parsePrimaryExpression); - } - - for (;;) { - if (match('.')) { - isBindingElement = false; - isAssignmentTarget = true; - property = parseNonComputedMember(); - expr = new WrappingNode(startToken).finishMemberExpression('.', expr, property); - } else if (match('(')) { - isBindingElement = false; - isAssignmentTarget = false; - args = parseArguments(); - expr = new WrappingNode(startToken).finishCallExpression(expr, args); - } else if (match('[')) { - isBindingElement = false; - isAssignmentTarget = true; - property = parseComputedMember(); - expr = new WrappingNode(startToken).finishMemberExpression('[', expr, property); - } else if (lookahead.type === Token.Template && lookahead.head) { - quasi = parseTemplateLiteral(); - expr = new WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi); - } else { - break; - } - } - state.allowIn = previousAllowIn; - - return expr; - } - - // ECMA-262 12.3 Left-Hand-Side Expressions - - function parseLeftHandSideExpression() { - var quasi, expr, property, startToken; - assert(state.allowIn, 'callee of new expression always allow in keyword.'); - - startToken = lookahead; - - if (matchKeyword('super') && state.inFunctionBody) { - expr = new Node(); - lex(); - expr = expr.finishSuper(); - if (!match('[') && !match('.')) { - throwUnexpectedToken(lookahead); - } - } else { - expr = inheritCoverGrammar(matchKeyword('new') ? parseNewExpression : parsePrimaryExpression); - } - - for (;;) { - if (match('[')) { - isBindingElement = false; - isAssignmentTarget = true; - property = parseComputedMember(); - expr = new WrappingNode(startToken).finishMemberExpression('[', expr, property); - } else if (match('.')) { - isBindingElement = false; - isAssignmentTarget = true; - property = parseNonComputedMember(); - expr = new WrappingNode(startToken).finishMemberExpression('.', expr, property); - } else if (lookahead.type === Token.Template && lookahead.head) { - quasi = parseTemplateLiteral(); - expr = new WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi); - } else { - break; - } - } - return expr; - } - - // ECMA-262 12.4 Postfix Expressions - - function parsePostfixExpression() { - var expr, token, startToken = lookahead; - - expr = inheritCoverGrammar(parseLeftHandSideExpressionAllowCall); - - if (!hasLineTerminator && lookahead.type === Token.Punctuator) { - if (match('++') || match('--')) { - // ECMA-262 11.3.1, 11.3.2 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - tolerateError(Messages.StrictLHSPostfix); - } - - if (!isAssignmentTarget) { - tolerateError(Messages.InvalidLHSInAssignment); - } - - isAssignmentTarget = isBindingElement = false; - - token = lex(); - expr = new WrappingNode(startToken).finishPostfixExpression(token.value, expr); - } - } - - return expr; - } - - // ECMA-262 12.5 Unary Operators - - function parseUnaryExpression() { - var token, expr, startToken; - - if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { - expr = parsePostfixExpression(); - } else if (match('++') || match('--')) { - startToken = lookahead; - token = lex(); - expr = inheritCoverGrammar(parseUnaryExpression); - // ECMA-262 11.4.4, 11.4.5 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - tolerateError(Messages.StrictLHSPrefix); - } - - if (!isAssignmentTarget) { - tolerateError(Messages.InvalidLHSInAssignment); - } - expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); - isAssignmentTarget = isBindingElement = false; - } else if (match('+') || match('-') || match('~') || match('!')) { - startToken = lookahead; - token = lex(); - expr = inheritCoverGrammar(parseUnaryExpression); - expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); - isAssignmentTarget = isBindingElement = false; - } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { - startToken = lookahead; - token = lex(); - expr = inheritCoverGrammar(parseUnaryExpression); - expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); - if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { - tolerateError(Messages.StrictDelete); - } - isAssignmentTarget = isBindingElement = false; - } else { - expr = parsePostfixExpression(); - } - - return expr; - } - - function binaryPrecedence(token, allowIn) { - var prec = 0; - - if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { - return 0; - } - - switch (token.value) { - case '||': - prec = 1; - break; - - case '&&': - prec = 2; - break; - - case '|': - prec = 3; - break; - - case '^': - prec = 4; - break; - - case '&': - prec = 5; - break; - - case '==': - case '!=': - case '===': - case '!==': - prec = 6; - break; - - case '<': - case '>': - case '<=': - case '>=': - case 'instanceof': - prec = 7; - break; - - case 'in': - prec = allowIn ? 7 : 0; - break; - - case '<<': - case '>>': - case '>>>': - prec = 8; - break; - - case '+': - case '-': - prec = 9; - break; - - case '*': - case '/': - case '%': - prec = 11; - break; - - default: - break; - } - - return prec; - } - - // ECMA-262 12.6 Multiplicative Operators - // ECMA-262 12.7 Additive Operators - // ECMA-262 12.8 Bitwise Shift Operators - // ECMA-262 12.9 Relational Operators - // ECMA-262 12.10 Equality Operators - // ECMA-262 12.11 Binary Bitwise Operators - // ECMA-262 12.12 Binary Logical Operators - - function parseBinaryExpression() { - var marker, markers, expr, token, prec, stack, right, operator, left, i; - - marker = lookahead; - left = inheritCoverGrammar(parseUnaryExpression); - - token = lookahead; - prec = binaryPrecedence(token, state.allowIn); - if (prec === 0) { - return left; - } - isAssignmentTarget = isBindingElement = false; - token.prec = prec; - lex(); - - markers = [marker, lookahead]; - right = isolateCoverGrammar(parseUnaryExpression); - - stack = [left, token, right]; - - while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) { - - // Reduce: make a binary expression from the three topmost entries. - while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { - right = stack.pop(); - operator = stack.pop().value; - left = stack.pop(); - markers.pop(); - expr = new WrappingNode(markers[markers.length - 1]).finishBinaryExpression(operator, left, right); - stack.push(expr); - } - - // Shift. - token = lex(); - token.prec = prec; - stack.push(token); - markers.push(lookahead); - expr = isolateCoverGrammar(parseUnaryExpression); - stack.push(expr); - } - - // Final reduce to clean-up the stack. - i = stack.length - 1; - expr = stack[i]; - markers.pop(); - while (i > 1) { - expr = new WrappingNode(markers.pop()).finishBinaryExpression(stack[i - 1].value, stack[i - 2], expr); - i -= 2; - } - - return expr; - } - - - // ECMA-262 12.13 Conditional Operator - - function parseConditionalExpression() { - var expr, previousAllowIn, consequent, alternate, startToken; - - startToken = lookahead; - - expr = inheritCoverGrammar(parseBinaryExpression); - if (match('?')) { - lex(); - previousAllowIn = state.allowIn; - state.allowIn = true; - consequent = isolateCoverGrammar(parseAssignmentExpression); - state.allowIn = previousAllowIn; - expect(':'); - alternate = isolateCoverGrammar(parseAssignmentExpression); - - expr = new WrappingNode(startToken).finishConditionalExpression(expr, consequent, alternate); - isAssignmentTarget = isBindingElement = false; - } - - return expr; - } - - // ECMA-262 14.2 Arrow Function Definitions - - function parseConciseBody() { - if (match('{')) { - return parseFunctionSourceElements(); - } - return isolateCoverGrammar(parseAssignmentExpression); - } - - function checkPatternParam(options, param) { - var i; - switch (param.type) { - case Syntax.Identifier: - validateParam(options, param, param.name); - break; - case Syntax.RestElement: - checkPatternParam(options, param.argument); - break; - case Syntax.AssignmentPattern: - checkPatternParam(options, param.left); - break; - case Syntax.ArrayPattern: - for (i = 0; i < param.elements.length; i++) { - if (param.elements[i] !== null) { - checkPatternParam(options, param.elements[i]); - } - } - break; - case Syntax.YieldExpression: - break; - default: - assert(param.type === Syntax.ObjectPattern, 'Invalid type'); - for (i = 0; i < param.properties.length; i++) { - checkPatternParam(options, param.properties[i].value); - } - break; - } - } - function reinterpretAsCoverFormalsList(expr) { - var i, len, param, params, defaults, defaultCount, options, token; - - defaults = []; - defaultCount = 0; - params = [expr]; - - switch (expr.type) { - case Syntax.Identifier: - break; - case PlaceHolders.ArrowParameterPlaceHolder: - params = expr.params; - break; - default: - return null; - } - - options = { - paramSet: {} - }; - - for (i = 0, len = params.length; i < len; i += 1) { - param = params[i]; - switch (param.type) { - case Syntax.AssignmentPattern: - params[i] = param.left; - if (param.right.type === Syntax.YieldExpression) { - if (param.right.argument) { - throwUnexpectedToken(lookahead); - } - param.right.type = Syntax.Identifier; - param.right.name = 'yield'; - delete param.right.argument; - delete param.right.delegate; - } - defaults.push(param.right); - ++defaultCount; - checkPatternParam(options, param.left); - break; - default: - checkPatternParam(options, param); - params[i] = param; - defaults.push(null); - break; - } - } - - if (strict || !state.allowYield) { - for (i = 0, len = params.length; i < len; i += 1) { - param = params[i]; - if (param.type === Syntax.YieldExpression) { - throwUnexpectedToken(lookahead); - } - } - } - - if (options.message === Messages.StrictParamDupe) { - token = strict ? options.stricted : options.firstRestricted; - throwUnexpectedToken(token, options.message); - } - - if (defaultCount === 0) { - defaults = []; - } - - return { - params: params, - defaults: defaults, - stricted: options.stricted, - firstRestricted: options.firstRestricted, - message: options.message - }; - } - - function parseArrowFunctionExpression(options, node) { - var previousStrict, previousAllowYield, body; - - if (hasLineTerminator) { - tolerateUnexpectedToken(lookahead); - } - expect('=>'); - - previousStrict = strict; - previousAllowYield = state.allowYield; - state.allowYield = true; - - body = parseConciseBody(); - - if (strict && options.firstRestricted) { - throwUnexpectedToken(options.firstRestricted, options.message); - } - if (strict && options.stricted) { - tolerateUnexpectedToken(options.stricted, options.message); - } - - strict = previousStrict; - state.allowYield = previousAllowYield; - - return node.finishArrowFunctionExpression(options.params, options.defaults, body, body.type !== Syntax.BlockStatement); - } - - // ECMA-262 14.4 Yield expression - - function parseYieldExpression() { - var argument, expr, delegate, previousAllowYield; - - argument = null; - expr = new Node(); - - expectKeyword('yield'); - - if (!hasLineTerminator) { - previousAllowYield = state.allowYield; - state.allowYield = false; - delegate = match('*'); - if (delegate) { - lex(); - argument = parseAssignmentExpression(); - } else { - if (!match(';') && !match('}') && !match(')') && lookahead.type !== Token.EOF) { - argument = parseAssignmentExpression(); - } - } - state.allowYield = previousAllowYield; - } - - return expr.finishYieldExpression(argument, delegate); - } - - // ECMA-262 12.14 Assignment Operators - - function parseAssignmentExpression() { - var token, expr, right, list, startToken; - - startToken = lookahead; - token = lookahead; - - if (!state.allowYield && matchKeyword('yield')) { - return parseYieldExpression(); - } - - expr = parseConditionalExpression(); - - if (expr.type === PlaceHolders.ArrowParameterPlaceHolder || match('=>')) { - isAssignmentTarget = isBindingElement = false; - list = reinterpretAsCoverFormalsList(expr); - - if (list) { - firstCoverInitializedNameError = null; - return parseArrowFunctionExpression(list, new WrappingNode(startToken)); - } - - return expr; - } - - if (matchAssign()) { - if (!isAssignmentTarget) { - tolerateError(Messages.InvalidLHSInAssignment); - } - - // ECMA-262 12.1.1 - if (strict && expr.type === Syntax.Identifier) { - if (isRestrictedWord(expr.name)) { - tolerateUnexpectedToken(token, Messages.StrictLHSAssignment); - } - if (isStrictModeReservedWord(expr.name)) { - tolerateUnexpectedToken(token, Messages.StrictReservedWord); - } - } - - if (!match('=')) { - isAssignmentTarget = isBindingElement = false; - } else { - reinterpretExpressionAsPattern(expr); - } - - token = lex(); - right = isolateCoverGrammar(parseAssignmentExpression); - expr = new WrappingNode(startToken).finishAssignmentExpression(token.value, expr, right); - firstCoverInitializedNameError = null; - } - - return expr; - } - - // ECMA-262 12.15 Comma Operator - - function parseExpression() { - var expr, startToken = lookahead, expressions; - - expr = isolateCoverGrammar(parseAssignmentExpression); - - if (match(',')) { - expressions = [expr]; - - while (startIndex < length) { - if (!match(',')) { - break; - } - lex(); - expressions.push(isolateCoverGrammar(parseAssignmentExpression)); - } - - expr = new WrappingNode(startToken).finishSequenceExpression(expressions); - } - - return expr; - } - - // ECMA-262 13.2 Block - - function parseStatementListItem() { - if (lookahead.type === Token.Keyword) { - switch (lookahead.value) { - case 'export': - if (state.sourceType !== 'module') { - tolerateUnexpectedToken(lookahead, Messages.IllegalExportDeclaration); - } - return parseExportDeclaration(); - case 'import': - if (state.sourceType !== 'module') { - tolerateUnexpectedToken(lookahead, Messages.IllegalImportDeclaration); - } - return parseImportDeclaration(); - case 'const': - case 'let': - return parseLexicalDeclaration({inFor: false}); - case 'function': - return parseFunctionDeclaration(new Node()); - case 'class': - return parseClassDeclaration(); - } - } - - return parseStatement(); - } - - function parseStatementList() { - var list = []; - while (startIndex < length) { - if (match('}')) { - break; - } - list.push(parseStatementListItem()); - } - - return list; - } - - function parseBlock() { - var block, node = new Node(); - - expect('{'); - - block = parseStatementList(); - - expect('}'); - - return node.finishBlockStatement(block); - } - - // ECMA-262 13.3.2 Variable Statement - - function parseVariableIdentifier(kind) { - var token, node = new Node(); - - token = lex(); - - if (token.type === Token.Keyword && token.value === 'yield') { - if (strict) { - tolerateUnexpectedToken(token, Messages.StrictReservedWord); - } if (!state.allowYield) { - throwUnexpectedToken(token); - } - } else if (token.type !== Token.Identifier) { - if (strict && token.type === Token.Keyword && isStrictModeReservedWord(token.value)) { - tolerateUnexpectedToken(token, Messages.StrictReservedWord); - } else { - if (strict || token.value !== 'let' || kind !== 'var') { - throwUnexpectedToken(token); - } - } - } else if (state.sourceType === 'module' && token.type === Token.Identifier && token.value === 'await') { - tolerateUnexpectedToken(token); - } - - return node.finishIdentifier(token.value); - } - - function parseVariableDeclaration(options) { - var init = null, id, node = new Node(), params = []; - - id = parsePattern(params, 'var'); - - // ECMA-262 12.2.1 - if (strict && isRestrictedWord(id.name)) { - tolerateError(Messages.StrictVarName); - } - - if (match('=')) { - lex(); - init = isolateCoverGrammar(parseAssignmentExpression); - } else if (id.type !== Syntax.Identifier && !options.inFor) { - expect('='); - } - - return node.finishVariableDeclarator(id, init); - } - - function parseVariableDeclarationList(options) { - var list = []; - - do { - list.push(parseVariableDeclaration({ inFor: options.inFor })); - if (!match(',')) { - break; - } - lex(); - } while (startIndex < length); - - return list; - } - - function parseVariableStatement(node) { - var declarations; - - expectKeyword('var'); - - declarations = parseVariableDeclarationList({ inFor: false }); - - consumeSemicolon(); - - return node.finishVariableDeclaration(declarations); - } - - // ECMA-262 13.3.1 Let and Const Declarations - - function parseLexicalBinding(kind, options) { - var init = null, id, node = new Node(), params = []; - - id = parsePattern(params, kind); - - // ECMA-262 12.2.1 - if (strict && id.type === Syntax.Identifier && isRestrictedWord(id.name)) { - tolerateError(Messages.StrictVarName); - } - - if (kind === 'const') { - if (!matchKeyword('in') && !matchContextualKeyword('of')) { - expect('='); - init = isolateCoverGrammar(parseAssignmentExpression); - } - } else if ((!options.inFor && id.type !== Syntax.Identifier) || match('=')) { - expect('='); - init = isolateCoverGrammar(parseAssignmentExpression); - } - - return node.finishVariableDeclarator(id, init); - } - - function parseBindingList(kind, options) { - var list = []; - - do { - list.push(parseLexicalBinding(kind, options)); - if (!match(',')) { - break; - } - lex(); - } while (startIndex < length); - - return list; - } - - function parseLexicalDeclaration(options) { - var kind, declarations, node = new Node(); - - kind = lex().value; - assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const'); - - declarations = parseBindingList(kind, options); - - consumeSemicolon(); - - return node.finishLexicalDeclaration(declarations, kind); - } - - function parseRestElement(params) { - var param, node = new Node(); - - lex(); - - if (match('{')) { - throwError(Messages.ObjectPatternAsRestParameter); - } - - params.push(lookahead); - - param = parseVariableIdentifier(); - - if (match('=')) { - throwError(Messages.DefaultRestParameter); - } - - if (!match(')')) { - throwError(Messages.ParameterAfterRestParameter); - } - - return node.finishRestElement(param); - } - - // ECMA-262 13.4 Empty Statement - - function parseEmptyStatement(node) { - expect(';'); - return node.finishEmptyStatement(); - } - - // ECMA-262 12.4 Expression Statement - - function parseExpressionStatement(node) { - var expr = parseExpression(); - consumeSemicolon(); - return node.finishExpressionStatement(expr); - } - - // ECMA-262 13.6 If statement - - function parseIfStatement(node) { - var test, consequent, alternate; - - expectKeyword('if'); - - expect('('); - - test = parseExpression(); - - expect(')'); - - consequent = parseStatement(); - - if (matchKeyword('else')) { - lex(); - alternate = parseStatement(); - } else { - alternate = null; - } - - return node.finishIfStatement(test, consequent, alternate); - } - - // ECMA-262 13.7 Iteration Statements - - function parseDoWhileStatement(node) { - var body, test, oldInIteration; - - expectKeyword('do'); - - oldInIteration = state.inIteration; - state.inIteration = true; - - body = parseStatement(); - - state.inIteration = oldInIteration; - - expectKeyword('while'); - - expect('('); - - test = parseExpression(); - - expect(')'); - - if (match(';')) { - lex(); - } - - return node.finishDoWhileStatement(body, test); - } - - function parseWhileStatement(node) { - var test, body, oldInIteration; - - expectKeyword('while'); - - expect('('); - - test = parseExpression(); - - expect(')'); - - oldInIteration = state.inIteration; - state.inIteration = true; - - body = parseStatement(); - - state.inIteration = oldInIteration; - - return node.finishWhileStatement(test, body); - } - - function parseForStatement(node) { - var init, forIn, initSeq, initStartToken, test, update, left, right, kind, declarations, - body, oldInIteration, previousAllowIn = state.allowIn; - - init = test = update = null; - forIn = true; - - expectKeyword('for'); - - expect('('); - - if (match(';')) { - lex(); - } else { - if (matchKeyword('var')) { - init = new Node(); - lex(); - - state.allowIn = false; - declarations = parseVariableDeclarationList({ inFor: true }); - state.allowIn = previousAllowIn; - - if (declarations.length === 1 && matchKeyword('in')) { - init = init.finishVariableDeclaration(declarations); - lex(); - left = init; - right = parseExpression(); - init = null; - } else if (declarations.length === 1 && declarations[0].init === null && matchContextualKeyword('of')) { - init = init.finishVariableDeclaration(declarations); - lex(); - left = init; - right = parseAssignmentExpression(); - init = null; - forIn = false; - } else { - init = init.finishVariableDeclaration(declarations); - expect(';'); - } - } else if (matchKeyword('const') || matchKeyword('let')) { - init = new Node(); - kind = lex().value; - - state.allowIn = false; - declarations = parseBindingList(kind, {inFor: true}); - state.allowIn = previousAllowIn; - - if (declarations.length === 1 && declarations[0].init === null && matchKeyword('in')) { - init = init.finishLexicalDeclaration(declarations, kind); - lex(); - left = init; - right = parseExpression(); - init = null; - } else if (declarations.length === 1 && declarations[0].init === null && matchContextualKeyword('of')) { - init = init.finishLexicalDeclaration(declarations, kind); - lex(); - left = init; - right = parseAssignmentExpression(); - init = null; - forIn = false; - } else { - consumeSemicolon(); - init = init.finishLexicalDeclaration(declarations, kind); - } - } else { - initStartToken = lookahead; - state.allowIn = false; - init = inheritCoverGrammar(parseAssignmentExpression); - state.allowIn = previousAllowIn; - - if (matchKeyword('in')) { - if (!isAssignmentTarget) { - tolerateError(Messages.InvalidLHSInForIn); - } - - lex(); - reinterpretExpressionAsPattern(init); - left = init; - right = parseExpression(); - init = null; - } else if (matchContextualKeyword('of')) { - if (!isAssignmentTarget) { - tolerateError(Messages.InvalidLHSInForLoop); - } - - lex(); - reinterpretExpressionAsPattern(init); - left = init; - right = parseAssignmentExpression(); - init = null; - forIn = false; - } else { - if (match(',')) { - initSeq = [init]; - while (match(',')) { - lex(); - initSeq.push(isolateCoverGrammar(parseAssignmentExpression)); - } - init = new WrappingNode(initStartToken).finishSequenceExpression(initSeq); - } - expect(';'); - } - } - } - - if (typeof left === 'undefined') { - - if (!match(';')) { - test = parseExpression(); - } - expect(';'); - - if (!match(')')) { - update = parseExpression(); - } - } - - expect(')'); - - oldInIteration = state.inIteration; - state.inIteration = true; - - body = isolateCoverGrammar(parseStatement); - - state.inIteration = oldInIteration; - - return (typeof left === 'undefined') ? - node.finishForStatement(init, test, update, body) : - forIn ? node.finishForInStatement(left, right, body) : - node.finishForOfStatement(left, right, body); - } - - // ECMA-262 13.8 The continue statement - - function parseContinueStatement(node) { - var label = null, key; - - expectKeyword('continue'); - - // Optimize the most common form: 'continue;'. - if (source.charCodeAt(startIndex) === 0x3B) { - lex(); - - if (!state.inIteration) { - throwError(Messages.IllegalContinue); - } - - return node.finishContinueStatement(null); - } - - if (hasLineTerminator) { - if (!state.inIteration) { - throwError(Messages.IllegalContinue); - } - - return node.finishContinueStatement(null); - } - - if (lookahead.type === Token.Identifier) { - label = parseVariableIdentifier(); - - key = '$' + label.name; - if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { - throwError(Messages.UnknownLabel, label.name); - } - } - - consumeSemicolon(); - - if (label === null && !state.inIteration) { - throwError(Messages.IllegalContinue); - } - - return node.finishContinueStatement(label); - } - - // ECMA-262 13.9 The break statement - - function parseBreakStatement(node) { - var label = null, key; - - expectKeyword('break'); - - // Catch the very common case first: immediately a semicolon (U+003B). - if (source.charCodeAt(lastIndex) === 0x3B) { - lex(); - - if (!(state.inIteration || state.inSwitch)) { - throwError(Messages.IllegalBreak); - } - - return node.finishBreakStatement(null); - } - - if (hasLineTerminator) { - if (!(state.inIteration || state.inSwitch)) { - throwError(Messages.IllegalBreak); - } - - return node.finishBreakStatement(null); - } - - if (lookahead.type === Token.Identifier) { - label = parseVariableIdentifier(); - - key = '$' + label.name; - if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { - throwError(Messages.UnknownLabel, label.name); - } - } - - consumeSemicolon(); - - if (label === null && !(state.inIteration || state.inSwitch)) { - throwError(Messages.IllegalBreak); - } - - return node.finishBreakStatement(label); - } - - // ECMA-262 13.10 The return statement - - function parseReturnStatement(node) { - var argument = null; - - expectKeyword('return'); - - if (!state.inFunctionBody) { - tolerateError(Messages.IllegalReturn); - } - - // 'return' followed by a space and an identifier is very common. - if (source.charCodeAt(lastIndex) === 0x20) { - if (isIdentifierStart(source.charCodeAt(lastIndex + 1))) { - argument = parseExpression(); - consumeSemicolon(); - return node.finishReturnStatement(argument); - } - } - - if (hasLineTerminator) { - // HACK - return node.finishReturnStatement(null); - } - - if (!match(';')) { - if (!match('}') && lookahead.type !== Token.EOF) { - argument = parseExpression(); - } - } - - consumeSemicolon(); - - return node.finishReturnStatement(argument); - } - - // ECMA-262 13.11 The with statement - - function parseWithStatement(node) { - var object, body; - - if (strict) { - tolerateError(Messages.StrictModeWith); - } - - expectKeyword('with'); - - expect('('); - - object = parseExpression(); - - expect(')'); - - body = parseStatement(); - - return node.finishWithStatement(object, body); - } - - // ECMA-262 13.12 The switch statement - - function parseSwitchCase() { - var test, consequent = [], statement, node = new Node(); - - if (matchKeyword('default')) { - lex(); - test = null; - } else { - expectKeyword('case'); - test = parseExpression(); - } - expect(':'); - - while (startIndex < length) { - if (match('}') || matchKeyword('default') || matchKeyword('case')) { - break; - } - statement = parseStatementListItem(); - consequent.push(statement); - } - - return node.finishSwitchCase(test, consequent); - } - - function parseSwitchStatement(node) { - var discriminant, cases, clause, oldInSwitch, defaultFound; - - expectKeyword('switch'); - - expect('('); - - discriminant = parseExpression(); - - expect(')'); - - expect('{'); - - cases = []; - - if (match('}')) { - lex(); - return node.finishSwitchStatement(discriminant, cases); - } - - oldInSwitch = state.inSwitch; - state.inSwitch = true; - defaultFound = false; - - while (startIndex < length) { - if (match('}')) { - break; - } - clause = parseSwitchCase(); - if (clause.test === null) { - if (defaultFound) { - throwError(Messages.MultipleDefaultsInSwitch); - } - defaultFound = true; - } - cases.push(clause); - } - - state.inSwitch = oldInSwitch; - - expect('}'); - - return node.finishSwitchStatement(discriminant, cases); - } - - // ECMA-262 13.14 The throw statement - - function parseThrowStatement(node) { - var argument; - - expectKeyword('throw'); - - if (hasLineTerminator) { - throwError(Messages.NewlineAfterThrow); - } - - argument = parseExpression(); - - consumeSemicolon(); - - return node.finishThrowStatement(argument); - } - - // ECMA-262 13.15 The try statement - - function parseCatchClause() { - var param, params = [], paramMap = {}, key, i, body, node = new Node(); - - expectKeyword('catch'); - - expect('('); - if (match(')')) { - throwUnexpectedToken(lookahead); - } - - param = parsePattern(params); - log(JSON.stringify(param)) - for (i = 0; i < params.length; i++) { - key = '$' + params[i].value; - if (Object.prototype.hasOwnProperty.call(paramMap, key)) { - tolerateError(Messages.DuplicateBinding, params[i].value); - } - paramMap[key] = true; - } - - // ECMA-262 12.14.1 - if (strict && isRestrictedWord(param.name)) { - tolerateError(Messages.StrictCatchVariable); - } - - expect(')'); - body = parseBlock(); - return node.finishCatchClause(param, body); - } - - function parseTryStatement(node) { - var block, handler = null, finalizer = null; - - expectKeyword('try'); - - block = parseBlock(); - - if (matchKeyword('catch')) { - handler = parseCatchClause(); - } - - if (matchKeyword('finally')) { - lex(); - finalizer = parseBlock(); - } - - if (!handler && !finalizer) { - throwError(Messages.NoCatchOrFinally); - } - - return node.finishTryStatement(block, handler, finalizer); - } - - // ECMA-262 13.16 The debugger statement - - function parseDebuggerStatement(node) { - expectKeyword('debugger'); - - consumeSemicolon(); - - return node.finishDebuggerStatement(); - } - - // 13 Statements - - function parseStatement() { - var type = lookahead.type, - expr, - labeledBody, - key, - node; - - if (type === Token.EOF) { - throwUnexpectedToken(lookahead); - } - - if (type === Token.Punctuator && lookahead.value === '{') { - return parseBlock(); - } - isAssignmentTarget = isBindingElement = true; - node = new Node(); - - if (type === Token.Punctuator) { - switch (lookahead.value) { - case ';': - return parseEmptyStatement(node); - case '(': - return parseExpressionStatement(node); - default: - break; - } - } else if (type === Token.Keyword) { - switch (lookahead.value) { - case 'break': - return parseBreakStatement(node); - case 'continue': - return parseContinueStatement(node); - case 'debugger': - return parseDebuggerStatement(node); - case 'do': - return parseDoWhileStatement(node); - case 'for': - return parseForStatement(node); - case 'function': - return parseFunctionDeclaration(node); - case 'if': - return parseIfStatement(node); - case 'return': - return parseReturnStatement(node); - case 'switch': - return parseSwitchStatement(node); - case 'throw': - return parseThrowStatement(node); - case 'try': - return parseTryStatement(node); - case 'var': - return parseVariableStatement(node); - case 'while': - return parseWhileStatement(node); - case 'with': - return parseWithStatement(node); - default: - break; - } - } - - expr = parseExpression(); - - // ECMA-262 12.12 Labelled Statements - if ((expr.type === Syntax.Identifier) && match(':')) { - lex(); - - key = '$' + expr.name; - if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) { - throwError(Messages.Redeclaration, 'Label', expr.name); - } - - state.labelSet[key] = true; - labeledBody = parseStatement(); - delete state.labelSet[key]; - return node.finishLabeledStatement(expr, labeledBody); - } - - consumeSemicolon(); - - return node.finishExpressionStatement(expr); - } - - // ECMA-262 14.1 Function Definition - - function parseFunctionSourceElements() { - var statement, body = [], token, directive, firstRestricted, - oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesisCount, - node = new Node(); - - expect('{'); - - while (startIndex < length) { - if (lookahead.type !== Token.StringLiteral) { - break; - } - token = lookahead; - - statement = parseStatementListItem(); - body.push(statement); - if (statement.expression.type !== Syntax.Literal) { - // this is not directive - break; - } - directive = source.slice(token.start + 1, token.end - 1); - if (directive === 'use strict') { - strict = true; - if (firstRestricted) { - tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral); - } - } else { - if (!firstRestricted && token.octal) { - firstRestricted = token; - } - } - } - - oldLabelSet = state.labelSet; - oldInIteration = state.inIteration; - oldInSwitch = state.inSwitch; - oldInFunctionBody = state.inFunctionBody; - oldParenthesisCount = state.parenthesizedCount; - - state.labelSet = {}; - state.inIteration = false; - state.inSwitch = false; - state.inFunctionBody = true; - state.parenthesizedCount = 0; - - while (startIndex < length) { - if (match('}')) { - break; - } - body.push(parseStatementListItem()); - } - - expect('}'); - - state.labelSet = oldLabelSet; - state.inIteration = oldInIteration; - state.inSwitch = oldInSwitch; - state.inFunctionBody = oldInFunctionBody; - state.parenthesizedCount = oldParenthesisCount; - - return node.finishBlockStatement(body); - } - - function validateParam(options, param, name) { - var key = '$' + name; - if (strict) { - if (isRestrictedWord(name)) { - options.stricted = param; - options.message = Messages.StrictParamName; - } - if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { - options.stricted = param; - options.message = Messages.StrictParamDupe; - } - } else if (!options.firstRestricted) { - if (isRestrictedWord(name)) { - options.firstRestricted = param; - options.message = Messages.StrictParamName; - } else if (isStrictModeReservedWord(name)) { - options.firstRestricted = param; - options.message = Messages.StrictReservedWord; - } else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { - options.stricted = param; - options.message = Messages.StrictParamDupe; - } - } - options.paramSet[key] = true; - } - - function parseParam(options) { - var token, param, params = [], i, def; - - token = lookahead; - if (token.value === '...') { - param = parseRestElement(params); - validateParam(options, param.argument, param.argument.name); - options.params.push(param); - options.defaults.push(null); - return false; - } - - param = parsePatternWithDefault(params); - for (i = 0; i < params.length; i++) { - validateParam(options, params[i], params[i].value); - } - - if (param.type === Syntax.AssignmentPattern) { - def = param.right; - param = param.left; - ++options.defaultCount; - } - - options.params.push(param); - options.defaults.push(def); - - return !match(')'); - } - - function parseParams(firstRestricted) { - var options; - - options = { - params: [], - defaultCount: 0, - defaults: [], - firstRestricted: firstRestricted - }; - - expect('('); - - if (!match(')')) { - options.paramSet = {}; - while (startIndex < length) { - if (!parseParam(options)) { - break; - } - expect(','); - } - } - - expect(')'); - - if (options.defaultCount === 0) { - options.defaults = []; - } - - return { - params: options.params, - defaults: options.defaults, - stricted: options.stricted, - firstRestricted: options.firstRestricted, - message: options.message - }; - } - - function parseFunctionDeclaration(node, identifierIsOptional) { - var id = null, params = [], defaults = [], body, token, stricted, tmp, firstRestricted, message, previousStrict, - isGenerator, previousAllowYield; - - previousAllowYield = state.allowYield; - - expectKeyword('function'); - - isGenerator = match('*'); - if (isGenerator) { - lex(); - } - - if (!identifierIsOptional || !match('(')) { - token = lookahead; - id = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - tolerateUnexpectedToken(token, Messages.StrictFunctionName); - } - } else { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictFunctionName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } - } - } - - state.allowYield = !isGenerator; - tmp = parseParams(firstRestricted); - params = tmp.params; - defaults = tmp.defaults; - stricted = tmp.stricted; - firstRestricted = tmp.firstRestricted; - if (tmp.message) { - message = tmp.message; - } - - - previousStrict = strict; - body = parseFunctionSourceElements(); - if (strict && firstRestricted) { - throwUnexpectedToken(firstRestricted, message); - } - if (strict && stricted) { - tolerateUnexpectedToken(stricted, message); - } - - strict = previousStrict; - state.allowYield = previousAllowYield; - - return node.finishFunctionDeclaration(id, params, defaults, body, isGenerator); - } - - function parseFunctionExpression() { - var token, id = null, stricted, firstRestricted, message, tmp, - params = [], defaults = [], body, previousStrict, node = new Node(), - isGenerator, previousAllowYield; - - previousAllowYield = state.allowYield; - - expectKeyword('function'); - - isGenerator = match('*'); - if (isGenerator) { - lex(); - } - - state.allowYield = !isGenerator; - if (!match('(')) { - token = lookahead; - id = (!strict && !isGenerator && matchKeyword('yield')) ? parseNonComputedProperty() : parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - tolerateUnexpectedToken(token, Messages.StrictFunctionName); - } - } else { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictFunctionName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } - } - } - - tmp = parseParams(firstRestricted); - params = tmp.params; - defaults = tmp.defaults; - stricted = tmp.stricted; - firstRestricted = tmp.firstRestricted; - if (tmp.message) { - message = tmp.message; - } - - previousStrict = strict; - body = parseFunctionSourceElements(); - if (strict && firstRestricted) { - throwUnexpectedToken(firstRestricted, message); - } - if (strict && stricted) { - tolerateUnexpectedToken(stricted, message); - } - strict = previousStrict; - state.allowYield = previousAllowYield; - - return node.finishFunctionExpression(id, params, defaults, body, isGenerator); - } - - // ECMA-262 14.5 Class Definitions - - function parseClassBody() { - var classBody, token, isStatic, hasConstructor = false, body, method, computed, key; - - classBody = new Node(); - - expect('{'); - body = []; - while (!match('}')) { - if (match(';')) { - lex(); - } else { - method = new Node(); - token = lookahead; - isStatic = false; - computed = match('['); - if (match('*')) { - lex(); - } else { - key = parseObjectPropertyKey(); - if (key.name === 'static' && (lookaheadPropertyName() || match('*'))) { - token = lookahead; - isStatic = true; - computed = match('['); - if (match('*')) { - lex(); - } else { - key = parseObjectPropertyKey(); - } - } - } - method = tryParseMethodDefinition(token, key, computed, method); - if (method) { - method['static'] = isStatic; // jscs:ignore requireDotNotation - if (method.kind === 'init') { - method.kind = 'method'; - } - if (!isStatic) { - if (!method.computed && (method.key.name || method.key.value.toString()) === 'constructor') { - if (method.kind !== 'method' || !method.method || method.value.generator) { - throwUnexpectedToken(token, Messages.ConstructorSpecialMethod); - } - if (hasConstructor) { - throwUnexpectedToken(token, Messages.DuplicateConstructor); - } else { - hasConstructor = true; - } - method.kind = 'constructor'; - } - } else { - if (!method.computed && (method.key.name || method.key.value.toString()) === 'prototype') { - throwUnexpectedToken(token, Messages.StaticPrototype); - } - } - method.type = Syntax.MethodDefinition; - delete method.method; - delete method.shorthand; - body.push(method); - } else { - throwUnexpectedToken(lookahead); - } - } - } - lex(); - return classBody.finishClassBody(body); - } - - function parseClassDeclaration(identifierIsOptional) { - var id = null, superClass = null, classNode = new Node(), classBody, previousStrict = strict; - strict = true; - - expectKeyword('class'); - - if (!identifierIsOptional || lookahead.type === Token.Identifier) { - id = parseVariableIdentifier(); - } - - if (matchKeyword('extends')) { - lex(); - superClass = isolateCoverGrammar(parseLeftHandSideExpressionAllowCall); - } - classBody = parseClassBody(); - strict = previousStrict; - - return classNode.finishClassDeclaration(id, superClass, classBody); - } - - function parseClassExpression() { - var id = null, superClass = null, classNode = new Node(), classBody, previousStrict = strict; - strict = true; - - expectKeyword('class'); - - if (lookahead.type === Token.Identifier) { - id = parseVariableIdentifier(); - } - - if (matchKeyword('extends')) { - lex(); - superClass = isolateCoverGrammar(parseLeftHandSideExpressionAllowCall); - } - classBody = parseClassBody(); - strict = previousStrict; - - return classNode.finishClassExpression(id, superClass, classBody); - } - - // ECMA-262 15.2 Modules - - function parseModuleSpecifier() { - var node = new Node(); - - if (lookahead.type !== Token.StringLiteral) { - throwError(Messages.InvalidModuleSpecifier); - } - return node.finishLiteral(lex()); - } - - // ECMA-262 15.2.3 Exports - - function parseExportSpecifier() { - var exported, local, node = new Node(), def; - if (matchKeyword('default')) { - // export {default} from 'something'; - def = new Node(); - lex(); - local = def.finishIdentifier('default'); - } else { - local = parseVariableIdentifier(); - } - if (matchContextualKeyword('as')) { - lex(); - exported = parseNonComputedProperty(); - } - return node.finishExportSpecifier(local, exported); - } - - function parseExportNamedDeclaration(node) { - var declaration = null, - isExportFromIdentifier, - src = null, specifiers = []; - - // non-default export - if (lookahead.type === Token.Keyword) { - // covers: - // export var f = 1; - switch (lookahead.value) { - case 'let': - case 'const': - case 'var': - case 'class': - case 'function': - declaration = parseStatementListItem(); - return node.finishExportNamedDeclaration(declaration, specifiers, null); - } - } - - expect('{'); - while (!match('}')) { - isExportFromIdentifier = isExportFromIdentifier || matchKeyword('default'); - specifiers.push(parseExportSpecifier()); - if (!match('}')) { - expect(','); - if (match('}')) { - break; - } - } - } - expect('}'); - - if (matchContextualKeyword('from')) { - // covering: - // export {default} from 'foo'; - // export {foo} from 'foo'; - lex(); - src = parseModuleSpecifier(); - consumeSemicolon(); - } else if (isExportFromIdentifier) { - // covering: - // export {default}; // missing fromClause - throwError(lookahead.value ? - Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); - } else { - // cover - // export {foo}; - consumeSemicolon(); - } - return node.finishExportNamedDeclaration(declaration, specifiers, src); - } - - function parseExportDefaultDeclaration(node) { - var declaration = null, - expression = null; - - // covers: - // export default ... - expectKeyword('default'); - - if (matchKeyword('function')) { - // covers: - // export default function foo () {} - // export default function () {} - declaration = parseFunctionDeclaration(new Node(), true); - return node.finishExportDefaultDeclaration(declaration); - } - if (matchKeyword('class')) { - declaration = parseClassDeclaration(true); - return node.finishExportDefaultDeclaration(declaration); - } - - if (matchContextualKeyword('from')) { - throwError(Messages.UnexpectedToken, lookahead.value); - } - - // covers: - // export default {}; - // export default []; - // export default (1 + 2); - if (match('{')) { - expression = parseObjectInitializer(); - } else if (match('[')) { - expression = parseArrayInitializer(); - } else { - expression = parseAssignmentExpression(); - } - consumeSemicolon(); - return node.finishExportDefaultDeclaration(expression); - } - - function parseExportAllDeclaration(node) { - var src; - - // covers: - // export * from 'foo'; - expect('*'); - if (!matchContextualKeyword('from')) { - throwError(lookahead.value ? - Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); - } - lex(); - src = parseModuleSpecifier(); - consumeSemicolon(); - - return node.finishExportAllDeclaration(src); - } - - function parseExportDeclaration() { - var node = new Node(); - if (state.inFunctionBody) { - throwError(Messages.IllegalExportDeclaration); - } - - expectKeyword('export'); - - if (matchKeyword('default')) { - return parseExportDefaultDeclaration(node); - } - if (match('*')) { - return parseExportAllDeclaration(node); - } - return parseExportNamedDeclaration(node); - } - - // ECMA-262 15.2.2 Imports - - function parseImportSpecifier() { - // import {} ...; - var local, imported, node = new Node(); - - imported = parseNonComputedProperty(); - if (matchContextualKeyword('as')) { - lex(); - local = parseVariableIdentifier(); - } - - return node.finishImportSpecifier(local, imported); - } - - function parseNamedImports() { - var specifiers = []; - // {foo, bar as bas} - expect('{'); - while (!match('}')) { - specifiers.push(parseImportSpecifier()); - if (!match('}')) { - expect(','); - if (match('}')) { - break; - } - } - } - expect('}'); - return specifiers; - } - - function parseImportDefaultSpecifier() { - // import ...; - var local, node = new Node(); - - local = parseNonComputedProperty(); - - return node.finishImportDefaultSpecifier(local); - } - - function parseImportNamespaceSpecifier() { - // import <* as foo> ...; - var local, node = new Node(); - - expect('*'); - if (!matchContextualKeyword('as')) { - throwError(Messages.NoAsAfterImportNamespace); - } - lex(); - local = parseNonComputedProperty(); - - return node.finishImportNamespaceSpecifier(local); - } - - function parseImportDeclaration() { - var specifiers = [], src, node = new Node(); - - if (state.inFunctionBody) { - throwError(Messages.IllegalImportDeclaration); - } - - expectKeyword('import'); - - if (lookahead.type === Token.StringLiteral) { - // import 'foo'; - src = parseModuleSpecifier(); - } else { - - if (match('{')) { - // import {bar} - specifiers = specifiers.concat(parseNamedImports()); - } else if (match('*')) { - // import * as foo - specifiers.push(parseImportNamespaceSpecifier()); - } else if (isIdentifierName(lookahead) && !matchKeyword('default')) { - // import foo - specifiers.push(parseImportDefaultSpecifier()); - if (match(',')) { - lex(); - if (match('*')) { - // import foo, * as foo - specifiers.push(parseImportNamespaceSpecifier()); - } else if (match('{')) { - // import foo, {bar} - specifiers = specifiers.concat(parseNamedImports()); - } else { - throwUnexpectedToken(lookahead); - } - } - } else { - throwUnexpectedToken(lex()); - } - - if (!matchContextualKeyword('from')) { - throwError(lookahead.value ? - Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); - } - lex(); - src = parseModuleSpecifier(); - } - - consumeSemicolon(); - return node.finishImportDeclaration(specifiers, src); - } - - // ECMA-262 15.1 Scripts - - function parseScriptBody() { - var statement, body = [], token, directive, firstRestricted; - - while (startIndex < length) { - token = lookahead; - if (token.type !== Token.StringLiteral) { - break; - } - - statement = parseStatementListItem(); - body.push(statement); - if (statement.expression.type !== Syntax.Literal) { - // this is not directive - break; - } - directive = source.slice(token.start + 1, token.end - 1); - if (directive === 'use strict') { - strict = true; - if (firstRestricted) { - tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral); - } - } else { - if (!firstRestricted && token.octal) { - firstRestricted = token; - } - } - } - - while (startIndex < length) { - statement = parseStatementListItem(); - /* istanbul ignore if */ - if (typeof statement === 'undefined') { - break; - } - body.push(statement); - } - return body; - } - - function parseProgram() { - var body, node; - - peek(); - node = new Node(); - - body = parseScriptBody(); - return node.finishProgram(body, state.sourceType); - } - - function filterTokenLocation() { - var i, entry, token, tokens = []; - - for (i = 0; i < extra.tokens.length; ++i) { - entry = extra.tokens[i]; - token = { - type: entry.type, - value: entry.value - }; - if (entry.regex) { - token.regex = { - pattern: entry.regex.pattern, - flags: entry.regex.flags - }; - } - if (extra.range) { - token.range = entry.range; - } - if (extra.loc) { - token.loc = entry.loc; - } - tokens.push(token); - } - - extra.tokens = tokens; - } - - function tokenize(code, options) { - var toString, - tokens; - - toString = String; - if (typeof code !== 'string' && !(code instanceof String)) { - code = toString(code); - } - - source = code; - index = 0; - lineNumber = (source.length > 0) ? 1 : 0; - lineStart = 0; - startIndex = index; - startLineNumber = lineNumber; - startLineStart = lineStart; - length = source.length; - lookahead = null; - state = { - allowIn: true, - allowYield: true, - labelSet: {}, - inFunctionBody: false, - inIteration: false, - inSwitch: false, - lastCommentStart: -1, - curlyStack: [] - }; - - extra = {}; - - // Options matching. - options = options || {}; - - // Of course we collect tokens here. - options.tokens = true; - extra.tokens = []; - extra.tokenize = true; - // The following two fields are necessary to compute the Regex tokens. - extra.openParenToken = -1; - extra.openCurlyToken = -1; - - extra.range = (typeof options.range === 'boolean') && options.range; - extra.loc = (typeof options.loc === 'boolean') && options.loc; - - if (typeof options.comment === 'boolean' && options.comment) { - extra.comments = []; - } - if (typeof options.tolerant === 'boolean' && options.tolerant) { - extra.errors = []; - } - - try { - peek(); - if (lookahead.type === Token.EOF) { - return extra.tokens; - } - - lex(); - while (lookahead.type !== Token.EOF) { - try { - lex(); - } catch (lexError) { - if (extra.errors) { - recordError(lexError); - // We have to break on the first error - // to avoid infinite loops. - break; - } else { - throw lexError; - } - } - } - - filterTokenLocation(); - tokens = extra.tokens; - if (typeof extra.comments !== 'undefined') { - tokens.comments = extra.comments; - } - if (typeof extra.errors !== 'undefined') { - tokens.errors = extra.errors; - } - } catch (e) { - throw e; - } finally { - extra = {}; - } - return tokens; - } - - function parse(code, options) { - var program, toString; - - toString = String; - if (typeof code !== 'string' && !(code instanceof String)) { - code = toString(code); - } - - source = code; - index = 0; - lineNumber = (source.length > 0) ? 1 : 0; - lineStart = 0; - startIndex = index; - startLineNumber = lineNumber; - startLineStart = lineStart; - length = source.length; - lookahead = null; - state = { - allowIn: true, - allowYield: true, - labelSet: {}, - inFunctionBody: false, - inIteration: false, - inSwitch: false, - lastCommentStart: -1, - curlyStack: [], - sourceType: 'script' - }; - strict = false; - - extra = {}; - if (typeof options !== 'undefined') { - extra.range = (typeof options.range === 'boolean') && options.range; - extra.loc = (typeof options.loc === 'boolean') && options.loc; - extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment; - - if (extra.loc && options.source !== null && options.source !== undefined) { - extra.source = toString(options.source); - } - - if (typeof options.tokens === 'boolean' && options.tokens) { - extra.tokens = []; - } - if (typeof options.comment === 'boolean' && options.comment) { - extra.comments = []; - } - if (typeof options.tolerant === 'boolean' && options.tolerant) { - extra.errors = []; - } - if (extra.attachComment) { - extra.range = true; - extra.comments = []; - extra.bottomRightStack = []; - extra.trailingComments = []; - extra.leadingComments = []; - } - if (options.sourceType === 'module') { - // very restrictive condition for now - state.sourceType = options.sourceType; - strict = true; - } - } - - try { - program = parseProgram(); - if (typeof extra.comments !== 'undefined') { - program.comments = extra.comments; - } - if (typeof extra.tokens !== 'undefined') { - filterTokenLocation(); - program.tokens = extra.tokens; - } - if (typeof extra.errors !== 'undefined') { - program.errors = extra.errors; - } - } catch (e) { - throw e; - } finally { - extra = {}; - } - - return program; - } - - // Sync with *.json manifests. - exports.version = '2.6.0'; - - exports.tokenize = tokenize; - - exports.parse = parse; - - // Deep copy. - /* istanbul ignore next */ - exports.Syntax = (function () { - var name, types = {}; - - if (typeof Object.create === 'function') { - types = Object.create(null); - } - - for (name in Syntax) { - if (Syntax.hasOwnProperty(name)) { - types[name] = Syntax[name]; - } - } - - if (typeof Object.freeze === 'function') { - Object.freeze(types); - } - - return types; - }()); - -})); -/* vim: set sw=4 ts=4 et tw=80 : */ diff --git a/js2py/internals/fill_space.py b/js2py/internals/fill_space.py index a3058137..9aa9c4d2 100644 --- a/js2py/internals/fill_space.py +++ b/js2py/internals/fill_space.py @@ -39,7 +39,7 @@ def fill_proto(proto, proto_class, space): meth = e else: continue - meth_name = meth.__name__.strip('_') # RexExp._exec -> RegExp.exec + meth_name = meth.__name__.strip('_') # RexExp._exec -> RegExp.exec js_meth = space.NewFunction(meth, space.ctx, (), meth_name, False, ()) set_non_enumerable(proto, meth_name, js_meth) return proto @@ -48,21 +48,29 @@ def fill_proto(proto, proto_class, space): def easy_func(f, space): return space.NewFunction(f, space.ctx, (), f.__name__, False, ()) + def Empty(this, args): return undefined + def set_non_enumerable(obj, name, prop): - obj.define_own_property(unicode(name), {'value': prop, - 'writable': True, - 'enumerable': False, - 'configurable': True}, True) + obj.define_own_property( + unicode(name), { + 'value': prop, + 'writable': True, + 'enumerable': False, + 'configurable': True + }, True) def set_protected(obj, name, prop): - obj.define_own_property(unicode(name), {'value': prop, - 'writable': False, - 'enumerable': False, - 'configurable': False}, True) + obj.define_own_property( + unicode(name), { + 'value': prop, + 'writable': False, + 'enumerable': False, + 'configurable': False + }, True) def fill_space(space, byte_generator): @@ -77,17 +85,17 @@ def fill_space(space, byte_generator): # first init all protos, later take care of constructors and details # Function must be first obviously, we have to use a small trick to do that... - function_proto = space.NewFunction(Empty, space.ctx, (), 'Empty', False, ()) - space.FunctionPrototype = function_proto # this will fill the prototypes of the methods! + function_proto = space.NewFunction(Empty, space.ctx, (), 'Empty', False, + ()) + space.FunctionPrototype = function_proto # this will fill the prototypes of the methods! fill_proto(function_proto, FunctionPrototype, space) # Object next - object_proto = space.NewObject() # no proto + object_proto = space.NewObject() # no proto fill_proto(object_proto, ObjectPrototype, space) space.ObjectPrototype = object_proto function_proto.prototype = object_proto - # Number number_proto = space.NewObject() number_proto.prototype = object_proto @@ -130,7 +138,7 @@ def fill_space(space, byte_generator): isFinite = easy_func(jsutils.isFinite, space) # Error - error_proto= space.NewError(u'Error', u'') + error_proto = space.NewError(u'Error', u'') error_proto.prototype = object_proto error_proto.put(u'name', u'Error') fill_proto(error_proto, ErrorPrototype, space) @@ -144,9 +152,11 @@ def creator(this, args): else: msg = u'' return space.NewError(typ, msg) + j = easy_func(creator, space) j.name = unicode(typ) j.prototype = space.ERROR_TYPES[typ] + def new_create(args, space): message = get_arg(args, 0) if not is_undefined(message): @@ -154,24 +164,20 @@ def new_create(args, space): else: msg = u'' return space.NewError(typ, msg) + j.create = new_create return j - # fill remaining error types error_constructors = {} - for err_type_name in ( - u'Error', - u'EvalError', - u'RangeError', - u'ReferenceError', - u'SyntaxError', - u'TypeError', - u'URIError'): + for err_type_name in (u'Error', u'EvalError', u'RangeError', + u'ReferenceError', u'SyntaxError', u'TypeError', + u'URIError'): extra_err = space.NewError(u'Error', u'') extra_err.put(u'name', err_type_name) - setattr(space, err_type_name+u'Prototype', extra_err) - error_constructors[err_type_name] = construct_constructor(err_type_name) + setattr(space, err_type_name + u'Prototype', extra_err) + error_constructors[err_type_name] = construct_constructor( + err_type_name) assert space.TypeErrorPrototype is not None # RegExp @@ -180,11 +186,8 @@ def new_create(args, space): fill_proto(regexp_proto, RegExpPrototype, space) space.RegExpPrototype = regexp_proto - - # Json - # now all these boring constructors... # Number @@ -204,7 +207,8 @@ def new_create(args, space): set_non_enumerable(string_proto, 'constructor', string) set_protected(string, 'prototype', string_proto) # string has an extra function - set_non_enumerable(string, 'fromCharCode', easy_func(jsstring.fromCharCode, space)) + set_non_enumerable(string, 'fromCharCode', + easy_func(jsstring.fromCharCode, space)) # Boolean boolean = easy_func(jsboolean.Boolean, space) @@ -244,34 +248,33 @@ def new_create(args, space): math = space.NewObject() math.Class = 'Math' fill_proto(math, jsmath.MathFunctions, space) - for k,v in jsmath.CONSTANTS.items(): + for k, v in jsmath.CONSTANTS.items(): set_protected(math, k, v) console = space.NewObject() fill_proto(console, jsconsole.ConsoleMethods, space) - # set global object builtins = { - 'String': string, - 'Number': number, - 'Boolean': boolean, - 'RegExp': regexp, - 'exports': convert_to_js_type({}, space), - 'Math': math, - #'Date', - 'Object': _object, - 'Function': function, - 'JSON': json, - 'Array': array, - 'parseFloat': parseFloat, - 'parseInt': parseInt, - 'isFinite': isFinite, - 'isNaN': isNaN, - 'eval': easy_func(jsfunction._eval, space), - 'console': console, - 'log': console.get(u'log'), - } + 'String': string, + 'Number': number, + 'Boolean': boolean, + 'RegExp': regexp, + 'exports': convert_to_js_type({}, space), + 'Math': math, + #'Date', + 'Object': _object, + 'Function': function, + 'JSON': json, + 'Array': array, + 'parseFloat': parseFloat, + 'parseInt': parseInt, + 'isFinite': isFinite, + 'isNaN': isNaN, + 'eval': easy_func(jsfunction._eval, space), + 'console': console, + 'log': console.get(u'log'), + } builtins.update(error_constructors) @@ -279,5 +282,3 @@ def new_create(args, space): set_protected(global_scope, 'Infinity', Infinity) for k, v in builtins.items(): set_non_enumerable(global_scope, k, v) - - diff --git a/js2py/internals/func_utils.py b/js2py/internals/func_utils.py index eae1e778..3c0b8d57 100644 --- a/js2py/internals/func_utils.py +++ b/js2py/internals/func_utils.py @@ -8,6 +8,7 @@ xrange = range unicode = str + def get_arg(arguments, n): if len(arguments) <= n: return undefined @@ -28,14 +29,21 @@ def convert_to_js_type(e, space=None): return unicode(t) elif t in (list, tuple): if space is None: - raise MakeError('TypeError', 'Actually an internal error, could not convert to js type because space not specified') + raise MakeError( + 'TypeError', + 'Actually an internal error, could not convert to js type because space not specified' + ) return space.ConstructArray(ensure_js_types(e, space=space)) - elif t==dict: + elif t == dict: if space is None: - raise MakeError('TypeError', 'Actually an internal error, could not convert to js type because space not specified') + raise MakeError( + 'TypeError', + 'Actually an internal error, could not convert to js type because space not specified' + ) new = {} - for k,v in e.items(): - new[to_string(convert_to_js_type(k, space))] = convert_to_js_type(v, space) + for k, v in e.items(): + new[to_string(convert_to_js_type(k, space))] = convert_to_js_type( + v, space) return space.ConstructObject(new) else: raise MakeError('TypeError', 'Could not convert to js type!') @@ -50,16 +58,16 @@ def is_js_type(e): return False - # todo optimise these 2! def js_array_to_tuple(arr): length = to_uint32(arr.get(u'length')) return tuple(arr.get(unicode(e)) for e in xrange(length)) + def js_array_to_list(arr): length = to_uint32(arr.get(u'length')) return [arr.get(unicode(e)) for e in xrange(length)] def js_arr_length(arr): - return to_uint32(arr.get(u'length')) \ No newline at end of file + return to_uint32(arr.get(u'length')) diff --git a/js2py/internals/opcodes.py b/js2py/internals/opcodes.py index c39dcc70..0f3127db 100644 --- a/js2py/internals/opcodes.py +++ b/js2py/internals/opcodes.py @@ -1,19 +1,24 @@ from operations import * from base import get_member, get_member_dot, PyJsFunction, Scope + class OP_CODE(object): _params = [] + # def eval(self, ctx): # raise def __repr__(self): - return self.__class__.__name__ + str(tuple([getattr(self, e) for e in self._params])) + return self.__class__.__name__ + str( + tuple([getattr(self, e) for e in self._params])) # --------------------- UNARY ---------------------- + class UNARY_OP(OP_CODE): _params = ['operator'] + def __init__(self, operator): self.operator = operator @@ -24,20 +29,23 @@ def eval(self, ctx): # special unary operations + class TYPEOF(OP_CODE): _params = ['identifier'] + def __init__(self, identifier): self.identifier = identifier def eval(self, ctx): # typeof something_undefined does not throw reference error - val = ctx.get(self.identifier, False) # <= this makes it slightly different! + val = ctx.get(self.identifier, + False) # <= this makes it slightly different! ctx.stack.append(typeof_uop(val)) - class POSTFIX(OP_CODE): _params = ['cb', 'ca', 'identifier'] + def __init__(self, post, incr, identifier): self.identifier = identifier self.cb = 1 if incr else -1 @@ -66,6 +74,7 @@ def eval(self, ctx): ctx.stack.append(target + self.ca) + class POSTFIX_MEMBER_DOT(OP_CODE): _params = ['cb', 'ca', 'prop'] @@ -77,7 +86,8 @@ def __init__(self, post, incr, prop): def eval(self, ctx): left = ctx.stack.pop() - target = to_number(get_member_dot(left, self.prop, ctx.space)) + self.cb + target = to_number(get_member_dot(left, self.prop, + ctx.space)) + self.cb if type(left) not in PRIMITIVES: left.put(self.prop, target) @@ -103,8 +113,10 @@ def eval(self, ctx): # --------------------- BITWISE ---------------------- + class BINARY_OP(OP_CODE): _params = ['operator'] + def __init__(self, operator): self.operator = operator @@ -113,15 +125,17 @@ def eval(self, ctx): left = ctx.stack.pop() ctx.stack.append(BINARY_OPERATIONS[self.operator](left, right)) -# &&, || and conditional are implemented in bytecode +# &&, || and conditional are implemented in bytecode # --------------------- JUMPS ---------------------- + # simple label that will be removed from code after compilation. labels ID will be translated # to source code position. class LABEL(OP_CODE): _params = ['num'] + def __init__(self, num): self.num = num @@ -129,15 +143,19 @@ def __init__(self, num): # I implemented interpreter in the way that when an integer is returned by eval operation the execution will jump # to the location of the label (it is loc = label_locations[label]) + class BASE_JUMP(OP_CODE): _params = ['label'] + def __init__(self, label): self.label = label + class JUMP(BASE_JUMP): def eval(self, ctx): return self.label + class JUMP_IF_TRUE(BASE_JUMP): def eval(self, ctx): val = ctx.stack.pop() @@ -153,48 +171,56 @@ def eval(self, ctx): ctx.stack.pop() return self.label + class JUMP_IF_TRUE_WITHOUT_POP(BASE_JUMP): def eval(self, ctx): val = ctx.stack[-1] if to_boolean(val): return self.label + class JUMP_IF_FALSE(BASE_JUMP): def eval(self, ctx): val = ctx.stack.pop() if not to_boolean(val): return self.label + class JUMP_IF_FALSE_WITHOUT_POP(BASE_JUMP): def eval(self, ctx): val = ctx.stack[-1] if not to_boolean(val): return self.label + class POP(OP_CODE): def eval(self, ctx): # todo remove this check later assert len(ctx.stack), 'Popped from empty stack!' del ctx.stack[-1] + # class REDUCE(OP_CODE): # def eval(self, ctx): # assert len(ctx.stack)==2 # ctx.stack[0] = ctx.stack[1] # del ctx.stack[1] - # --------------- LOADING -------------- class LOAD_NONE(OP_CODE): # be careful with this :) _params = [] + def eval(self, ctx): ctx.stack.append(None) -class LOAD_N_TUPLE(OP_CODE): # loads the tuple composed of n last elements on stack. elements are popped. +class LOAD_N_TUPLE( + OP_CODE +): # loads the tuple composed of n last elements on stack. elements are popped. _params = ['n'] + def __init__(self, n): self.n = n @@ -216,6 +242,7 @@ def eval(self, ctx): class LOAD_BOOLEAN(OP_CODE): _params = ['val'] + def __init__(self, val): assert val in (0, 1) self.val = bool(val) @@ -226,6 +253,7 @@ def eval(self, ctx): class LOAD_STRING(OP_CODE): _params = ['val'] + def __init__(self, val): assert isinstance(val, basestring) self.val = unicode(val) @@ -236,6 +264,7 @@ def eval(self, ctx): class LOAD_NUMBER(OP_CODE): _params = ['val'] + def __init__(self, val): assert isinstance(val, (float, int, long)) self.val = float(val) @@ -246,6 +275,7 @@ def eval(self, ctx): class LOAD_REGEXP(OP_CODE): _params = ['body', 'flags'] + def __init__(self, body, flags): self.body = body self.flags = flags @@ -257,20 +287,26 @@ def eval(self, ctx): class LOAD_FUNCTION(OP_CODE): _params = ['start', 'params', 'name', 'is_declaration', 'definitions'] + def __init__(self, start, params, name, is_declaration, definitions): assert type(start) == int self.start = start # its an ID of label pointing to the beginning of the function bytecode self.params = params self.name = name self.is_declaration = bool(is_declaration) - self.definitions = tuple(set(definitions+params)) + self.definitions = tuple(set(definitions + params)) def eval(self, ctx): - ctx.stack.append(ctx.space.NewFunction(self.start, ctx, self.params, self.name, self.is_declaration, self.definitions)) + ctx.stack.append( + ctx.space.NewFunction(self.start, ctx, self.params, self.name, + self.is_declaration, self.definitions)) class LOAD_OBJECT(OP_CODE): - _params = ['props'] # props are py string pairs (prop_name, kind): kind can be either i, g or s. (init, get, set) + _params = [ + 'props' + ] # props are py string pairs (prop_name, kind): kind can be either i, g or s. (init, get, set) + def __init__(self, props): self.num = len(props) self.props = props @@ -286,6 +322,7 @@ def eval(self, ctx): class LOAD_ARRAY(OP_CODE): _params = ['num'] + def __init__(self, num): self.num = num @@ -302,7 +339,7 @@ def eval(self, ctx): ctx.stack.append(ctx.THIS_BINDING) -class LOAD(OP_CODE): # todo check! +class LOAD(OP_CODE): # todo check! _params = ['identifier'] def __init__(self, identifier): @@ -322,6 +359,7 @@ def eval(self, ctx): class LOAD_MEMBER_DOT(OP_CODE): _params = ['prop'] + def __init__(self, prop): self.prop = prop @@ -330,12 +368,12 @@ def eval(self, ctx): ctx.stack.append(get_member_dot(obj, self.prop, ctx.space)) - # --------------- STORING -------------- class STORE(OP_CODE): _params = ['identifier'] + def __init__(self, identifier): self.identifier = identifier @@ -354,9 +392,11 @@ def eval(self, ctx): if typ in PRIMITIVES: prop = to_string(name) if typ == NULL_TYPE: - raise MakeError('TypeError', "Cannot set property '%s' of null" % prop) + raise MakeError('TypeError', + "Cannot set property '%s' of null" % prop) elif typ == UNDEFINED_TYPE: - raise MakeError('TypeError', "Cannot set property '%s' of undefined" % prop) + raise MakeError('TypeError', + "Cannot set property '%s' of undefined" % prop) # just ignore... else: left.put_member(name, value) @@ -366,6 +406,7 @@ def eval(self, ctx): class STORE_MEMBER_DOT(OP_CODE): _params = ['prop'] + def __init__(self, prop): self.prop = prop @@ -376,9 +417,12 @@ def eval(self, ctx): typ = type(left) if typ in PRIMITIVES: if typ == NULL_TYPE: - raise MakeError('TypeError', "Cannot set property '%s' of null" % self.prop) + raise MakeError('TypeError', + "Cannot set property '%s' of null" % self.prop) elif typ == UNDEFINED_TYPE: - raise MakeError('TypeError', "Cannot set property '%s' of undefined" % self.prop) + raise MakeError( + 'TypeError', + "Cannot set property '%s' of undefined" % self.prop) # just ignore... else: left.put(self.prop, value) @@ -401,6 +445,7 @@ def eval(self, ctx): class STORE_MEMBER_OP(OP_CODE): _params = ['op'] + def __init__(self, op): self.op = op @@ -412,18 +457,25 @@ def eval(self, ctx): typ = type(left) if typ in PRIMITIVES: if typ is NULL_TYPE: - raise MakeError('TypeError', "Cannot set property '%s' of null" % to_string(name)) + raise MakeError( + 'TypeError', + "Cannot set property '%s' of null" % to_string(name)) elif typ is UNDEFINED_TYPE: - raise MakeError('TypeError', "Cannot set property '%s' of undefined" % to_string(name)) - ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(left, name, ctx.space), value)) + raise MakeError( + 'TypeError', + "Cannot set property '%s' of undefined" % to_string(name)) + ctx.stack.append(BINARY_OPERATIONS[self.op](get_member( + left, name, ctx.space), value)) return else: - ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(left, name, ctx.space), value)) + ctx.stack.append(BINARY_OPERATIONS[self.op](get_member( + left, name, ctx.space), value)) left.put_member(name, ctx.stack[-1]) class STORE_MEMBER_DOT_OP(OP_CODE): _params = ['prop', 'op'] + def __init__(self, prop, op): self.prop = prop self.op = op @@ -435,22 +487,28 @@ def eval(self, ctx): typ = type(left) if typ in PRIMITIVES: if typ == NULL_TYPE: - raise MakeError('TypeError', "Cannot set property '%s' of null" % self.prop) + raise MakeError('TypeError', + "Cannot set property '%s' of null" % self.prop) elif typ == UNDEFINED_TYPE: - raise MakeError('TypeError', "Cannot set property '%s' of undefined" % self.prop) - ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(left, self.prop, ctx.space), value)) + raise MakeError( + 'TypeError', + "Cannot set property '%s' of undefined" % self.prop) + ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot( + left, self.prop, ctx.space), value)) return else: - ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(left, self.prop, ctx.space), value)) + ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot( + left, self.prop, ctx.space), value)) left.put(self.prop, ctx.stack[-1]) # --------------- CALLS -------------- + def bytecode_call(ctx, func, this, args): if type(func) is not PyJsFunction: - raise MakeError('TypeError', "%s is not a function" % Type(func) ) - if func.is_native: # call to built-in function or method + raise MakeError('TypeError', "%s is not a function" % Type(func)) + if func.is_native: # call to built-in function or method ctx.stack.append(func.call(this, args)) return None @@ -479,6 +537,7 @@ def eval(self, ctx): class CALL_METHOD_DOT(OP_CODE): _params = ['prop'] + def __init__(self, prop): self.prop = prop @@ -510,6 +569,7 @@ def eval(self, ctx): class CALL_METHOD_DOT_NO_ARGS(OP_CODE): _params = ['prop'] + def __init__(self, prop): self.prop = prop @@ -525,8 +585,11 @@ class NOP(OP_CODE): def eval(self, ctx): pass + class RETURN(OP_CODE): - def eval(self, ctx): # remember to load the return value on stack before using RETURN op. + def eval( + self, ctx + ): # remember to load the return value on stack before using RETURN op. return (None, None) @@ -534,16 +597,20 @@ class NEW(OP_CODE): def eval(self, ctx): args = ctx.stack.pop() constructor = ctx.stack.pop() - if type(constructor) in PRIMITIVES or not hasattr(constructor, 'create'): - raise MakeError('TypeError', '%s is not a constructor' % Type(constructor)) + if type(constructor) in PRIMITIVES or not hasattr( + constructor, 'create'): + raise MakeError('TypeError', + '%s is not a constructor' % Type(constructor)) ctx.stack.append(constructor.create(args, space=ctx.space)) class NEW_NO_ARGS(OP_CODE): def eval(self, ctx): constructor = ctx.stack.pop() - if type(constructor) in PRIMITIVES or not hasattr(constructor, 'create'): - raise MakeError('TypeError', '%s is not a constructor' % Type(constructor)) + if type(constructor) in PRIMITIVES or not hasattr( + constructor, 'create'): + raise MakeError('TypeError', + '%s is not a constructor' % Type(constructor)) ctx.stack.append(constructor.create((), space=ctx.space)) @@ -555,11 +622,14 @@ def eval(self, ctx): raise MakeError(None, None, ctx.stack.pop()) - - class TRY_CATCH_FINALLY(OP_CODE): - _params = ['try_label', 'catch_label', 'catch_variable', 'finally_label', 'finally_present', 'end_label'] - def __init__(self, try_label, catch_label, catch_variable, finally_label, finally_present, end_label): + _params = [ + 'try_label', 'catch_label', 'catch_variable', 'finally_label', + 'finally_present', 'end_label' + ] + + def __init__(self, try_label, catch_label, catch_variable, finally_label, + finally_present, end_label): self.try_label = try_label self.catch_label = catch_label self.catch_variable = catch_variable @@ -576,37 +646,44 @@ def eval(self, ctx): ctx.stack.pop() # execute try statement - try_status = ctx.space.exe.execute_fragment_under_context(ctx, self.try_label, self.catch_label) + try_status = ctx.space.exe.execute_fragment_under_context( + ctx, self.try_label, self.catch_label) errors = try_status[1] == 3 # catch if errors and self.catch_variable is not None: # generate catch block context... - catch_context = Scope({self.catch_variable: try_status[2].get_thrown_value(ctx.space)}, ctx.space, ctx) + catch_context = Scope({ + self.catch_variable: + try_status[2].get_thrown_value(ctx.space) + }, ctx.space, ctx) catch_context.THIS_BINDING = ctx.THIS_BINDING - catch_status = ctx.space.exe.execute_fragment_under_context(catch_context, self.catch_label, self.finally_label) + catch_status = ctx.space.exe.execute_fragment_under_context( + catch_context, self.catch_label, self.finally_label) else: catch_status = None # finally if self.finally_present: - finally_status = ctx.space.exe.execute_fragment_under_context(ctx, self.finally_label, self.end_label) + finally_status = ctx.space.exe.execute_fragment_under_context( + ctx, self.finally_label, self.end_label) else: finally_status = None # now return controls other_status = catch_status or try_status - if finally_status is None or (finally_status[1]==0 and other_status[1]!=0): + if finally_status is None or (finally_status[1] == 0 + and other_status[1] != 0): winning_status = other_status else: winning_status = finally_status val, typ, spec = winning_status - if typ == 0: # normal + if typ == 0: # normal ctx.stack.append(val) return - elif typ == 1: # return + elif typ == 1: # return ctx.stack.append(spec) return None, None # send return signal elif typ == 2: # jump outside @@ -619,22 +696,12 @@ def eval(self, ctx): raise RuntimeError('Invalid return code') - - - - - - - - - - - - # ------------ WITH + ITERATORS ---------- + class WITH(OP_CODE): _params = ['beg_label', 'end_label'] + def __init__(self, beg_label, end_label): self.beg_label = beg_label self.end_label = end_label @@ -642,9 +709,11 @@ def __init__(self, beg_label, end_label): def eval(self, ctx): obj = to_object(ctx.stack.pop(), ctx.space) - with_context = Scope(obj, ctx.space, ctx) # todo actually use the obj to modify the ctx + with_context = Scope( + obj, ctx.space, ctx) # todo actually use the obj to modify the ctx with_context.THIS_BINDING = ctx.THIS_BINDING - status = ctx.space.exe.execute_fragment_under_context(with_context, self.beg_label, self.end_label) + status = ctx.space.exe.execute_fragment_under_context( + with_context, self.beg_label, self.end_label) val, typ, spec = status @@ -669,6 +738,7 @@ def eval(self, ctx): class FOR_IN(OP_CODE): _params = ['name', 'body_start_label', 'continue_label', 'break_label'] + def __init__(self, name, body_start_label, continue_label, break_label): self.name = name self.body_start_label = body_start_label @@ -688,10 +758,13 @@ def eval(self, ctx): if not obj.own[e]['enumerable']: continue - ctx.put(self.name, e) # JS would have been so much nicer if this was ctx.space.put(self.name, obj.get(e)) + ctx.put( + self.name, e + ) # JS would have been so much nicer if this was ctx.space.put(self.name, obj.get(e)) # evaluate the body - status = ctx.space.exe.execute_fragment_under_context(ctx, self.body_start_label, self.break_label) + status = ctx.space.exe.execute_fragment_under_context( + ctx, self.body_start_label, self.break_label) val, typ, spec = status @@ -710,7 +783,7 @@ def eval(self, ctx): if spec == self.continue_label: # just a continue, perform next iteration as normal continue - return spec # break or smth, go there and finish the iteration + return spec # break or smth, go there and finish the iteration elif typ == 3: # exception # throw is made with empty stack as usual raise spec diff --git a/js2py/internals/operations.py b/js2py/internals/operations.py index 3bbda03d..d9875088 100644 --- a/js2py/internals/operations.py +++ b/js2py/internals/operations.py @@ -2,22 +2,25 @@ from simplex import * from conversions import * - # ------------------------------------------------------------------------------ # Unary operations + # -x def minus_uop(self): return -to_number(self) + # +x def plus_uop(self): # +u return to_number(self) + # !x def logical_negation_uop(self): # !u cant do 'not u' :( return not to_boolean(self) + # typeof x def typeof_uop(self): if is_callable(self): @@ -27,10 +30,12 @@ def typeof_uop(self): typ = u'object' # absolutely idiotic... return typ + # ~u def bit_invert_uop(self): return float(to_int32(float(~to_int32(self)))) + # void def void_op(self): return undefined @@ -42,7 +47,8 @@ def void_op(self): '!': logical_negation_uop, '~': bit_invert_uop, 'void': void_op, - 'typeof': typeof_uop, # this one only for member expressions! for identifiers its slightly different... + 'typeof': + typeof_uop, # this one only for member expressions! for identifiers its slightly different... } # ------------------------------------------------------------------------------ @@ -52,7 +58,6 @@ def void_op(self): # <<, >>, &, ^, |, ~ - # << def bit_lshift_op(self, other): lnum = to_int32(self) @@ -60,6 +65,7 @@ def bit_lshift_op(self, other): shiftCount = rnum & 0x1F return float(to_int32(float(lnum << shiftCount))) + # >> def bit_rshift_op(self, other): lnum = to_int32(self) @@ -67,6 +73,7 @@ def bit_rshift_op(self, other): shiftCount = rnum & 0x1F return float(to_int32(float(lnum >> shiftCount))) + # >>> def bit_bshift_op(self, other): lnum = to_uint32(self) @@ -74,27 +81,32 @@ def bit_bshift_op(self, other): shiftCount = rnum & 0x1F return float(to_uint32(float(lnum >> shiftCount))) + # & def bit_and_op(self, other): lnum = to_int32(self) rnum = to_int32(other) return float(to_int32(float(lnum & rnum))) + # ^ def bit_xor_op(self, other): lnum = to_int32(self) rnum = to_int32(other) return float(to_int32(float(lnum ^ rnum))) + # | def bit_or_op(self, other): lnum = to_int32(self) rnum = to_int32(other) return float(to_int32(float(lnum | rnum))) + # Additive operators # + and - are implemented here + # + def add_op(self, other): if type(self) is float and type(other) is float: @@ -108,17 +120,21 @@ def add_op(self, other): return to_string(a) + to_string(b) return to_number(a) + to_number(b) + # - def sub_op(self, other): return to_number(self) - to_number(other) + # Multiplicative operators # *, / and % are implemented here + # * def mul_op(self, other): return to_number(self) * to_number(other) + # / def div_op(self, other): a = to_number(self) @@ -129,6 +145,7 @@ def div_op(self, other): return NaN return Infinity if a > 0 else -Infinity + # % def mod_op(self, other): a = to_number(self) @@ -147,10 +164,10 @@ def mod_op(self, other): return float(pyres) - # Comparisons # <, <=, !=, ==, >=, > are implemented here. -def abstract_relational_comparison(self, other, self_first=True): # todo speed up! +def abstract_relational_comparison(self, other, + self_first=True): # todo speed up! ''' self= def greater_eq_op(self, other): res = abstract_relational_comparison(self, other, True) @@ -192,6 +212,7 @@ def greater_eq_op(self, other): return False return not res + # > def greater_op(self, other): res = abstract_relational_comparison(self, other, False) @@ -202,6 +223,7 @@ def greater_op(self, other): # equality + def abstract_equality_op(self, other): ''' returns the result of JS == compare. result is PyJs type: bool''' @@ -212,7 +234,8 @@ def abstract_equality_op(self, other): if tx == 'Number' or tx == 'String' or tx == 'Boolean': return self == other return self is other # Object - elif (tx == 'Undefined' and ty == 'Null') or (ty == 'Undefined' and tx == 'Null'): + elif (tx == 'Undefined' and ty == 'Null') or (ty == 'Undefined' + and tx == 'Null'): return True elif tx == 'Number' and ty == 'String': return abstract_equality_op(self, to_number(other)) @@ -229,6 +252,7 @@ def abstract_equality_op(self, other): else: return False + def abstract_inequality_op(self, other): return not abstract_equality_op(self, other) @@ -248,6 +272,7 @@ def strict_equality_op(self, other): def strict_inequality_op(self, other): return not strict_equality_op(self, other) + def instanceof_op(self, other): '''checks if self is instance of other''' if not hasattr(other, 'has_instance'): @@ -258,25 +283,24 @@ def instanceof_op(self, other): def in_op(self, other): '''checks if self is in other''' if not is_object(other): - raise MakeError('TypeError', "You can\'t use 'in' operator to search in non-objects") + raise MakeError( + 'TypeError', + "You can\'t use 'in' operator to search in non-objects") return other.has_property(to_string(self)) BINARY_OPERATIONS = { '+': add_op, '-': sub_op, - '*': mul_op, '/': div_op, '%': mod_op, - '<<': bit_lshift_op, '>>': bit_rshift_op, '>>>': bit_bshift_op, '|': bit_or_op, '&': bit_and_op, '^': bit_xor_op, - '==': abstract_equality_op, '!=': abstract_inequality_op, '===': strict_equality_op, @@ -285,8 +309,6 @@ def in_op(self, other): '<=': less_eq_op, '>': greater_op, '>=': greater_eq_op, - 'in': in_op, 'instanceof': instanceof_op, } - diff --git a/js2py/internals/prototypes/jsarray.py b/js2py/internals/prototypes/jsarray.py index bef77ab0..ace774ec 100644 --- a/js2py/internals/prototypes/jsarray.py +++ b/js2py/internals/prototypes/jsarray.py @@ -9,9 +9,9 @@ xrange = range import functools - ARR_STACK = set({}) + class ArrayPrototype: def toString(this, args): arr = to_object(this, args.space) @@ -35,7 +35,10 @@ def toLocaleString(this, args): cand = to_object(element, args.space) str_func = cand.get('toLocaleString') if not is_callable(str_func): - raise MakeError('TypeError', 'toLocaleString method of item at index %d is not callable' % i) + raise MakeError( + 'TypeError', + 'toLocaleString method of item at index %d is not callable' + % i) res.append(to_string(str_func.call(cand, ()))) return ','.join(res) @@ -45,13 +48,13 @@ def concat(this, args): items.extend(tuple(args)) A = [] for E in items: - if GetClass(E)=='Array': + if GetClass(E) == 'Array': k = 0 e_len = js_arr_length(E) - while k (arr_len - actual_delete_count + items_len): - array.delete(unicode(k-1)) + array.delete(unicode(k - 1)) k -= 1 # 13 - elif items_len>actual_delete_count: + elif items_len > actual_delete_count: k = arr_len - actual_delete_count - while k>actual_start: + while k > actual_start: fr = unicode(k + actual_delete_count - 1) to = unicode(k + items_len - 1) if array.has_property(fr): @@ -250,7 +264,7 @@ def indexOf(this, args): arr_len = js_arr_length(array) if arr_len == 0: return -1. - if len(args)>1: + if len(args) > 1: n = to_int(args[1]) else: n = 0 @@ -281,7 +295,7 @@ def lastIndexOf(this, args): else: n = arr_len - 1 if n >= 0: - k = min(n, arr_len-1) + k = min(n, arr_len - 1) else: k = arr_len - abs(n) while k >= 0: @@ -292,7 +306,6 @@ def lastIndexOf(this, args): k -= 1 return -1. - def every(this, args): array = to_object(this, args.space) callbackfn = get_arg(args, 0) @@ -301,15 +314,15 @@ def every(this, args): raise MakeError('TypeError', 'callbackfn must be a function') T = get_arg(args, 1) k = 0 - while k 1: # initial value present + if len(args) > 1: # initial value present accumulator = args[1] else: kPresent = False - while not kPresent and k=0: + while not kPresent and k >= 0: kPresent = array.has_property(unicode(k)) if kPresent: accumulator = array.get(unicode(k)) k -= 1 if not kPresent: - raise MakeError('TypeError', 'Reduce of empty array with no initial value') - while k>=0: + raise MakeError('TypeError', + 'Reduce of empty array with no initial value') + while k >= 0: if array.has_property(unicode(k)): kValue = array.get(unicode(k)) - accumulator = callbackfn.call(undefined, (accumulator, kValue, float(k), array)) + accumulator = callbackfn.call( + undefined, (accumulator, kValue, float(k), array)) k -= 1 return accumulator @@ -458,12 +482,8 @@ def sort_compare(a, b, comp): res = comp.call(undefined, (a, b)) return to_int(res) x, y = to_string(a), to_string(b) - if xy: + elif x > y: return 1 return 0 - - - - diff --git a/js2py/internals/prototypes/jsboolean.py b/js2py/internals/prototypes/jsboolean.py index f0d4681f..9aff9d2e 100644 --- a/js2py/internals/prototypes/jsboolean.py +++ b/js2py/internals/prototypes/jsboolean.py @@ -3,21 +3,20 @@ from ..conversions import * from ..func_utils import * + class BooleanPrototype: def toString(this, args): - if GetClass(this)!='Boolean': - raise MakeError('TypeError', 'Boolean.prototype.toString is not generic') + if GetClass(this) != 'Boolean': + raise MakeError('TypeError', + 'Boolean.prototype.toString is not generic') if is_object(this): this = this.value return u'true' if this else u'false' def valueOf(this, args): - if GetClass(this)!='Boolean': - raise MakeError('TypeError', 'Boolean.prototype.valueOf is not generic') + if GetClass(this) != 'Boolean': + raise MakeError('TypeError', + 'Boolean.prototype.valueOf is not generic') if is_object(this): this = this.value return this - - - - diff --git a/js2py/internals/prototypes/jserror.py b/js2py/internals/prototypes/jserror.py index 6a7dfc9d..c58dec49 100644 --- a/js2py/internals/prototypes/jserror.py +++ b/js2py/internals/prototypes/jserror.py @@ -2,10 +2,12 @@ from ..conversions import * from ..func_utils import * + class ErrorPrototype: def toString(this, args): - if Type(this)!='Object': - raise MakeError('TypeError', 'Error.prototype.toString called on non-object') + if Type(this) != 'Object': + raise MakeError('TypeError', + 'Error.prototype.toString called on non-object') name = this.get('name') name = u'Error' if is_undefined(name) else to_string(name) msg = this.get('message') diff --git a/js2py/internals/prototypes/jsfunction.py b/js2py/internals/prototypes/jsfunction.py index fe92014c..06ec6801 100644 --- a/js2py/internals/prototypes/jsfunction.py +++ b/js2py/internals/prototypes/jsfunction.py @@ -11,44 +11,51 @@ xrange = range unicode = str - # todo fix apply and bind -class FunctionPrototype: + +class FunctionPrototype: def toString(this, args): if not is_callable(this): - raise MakeError('TypeError', 'Function.prototype.toString is not generic') + raise MakeError('TypeError', + 'Function.prototype.toString is not generic') args = u', '.join(map(unicode, this.params)) - return u'function %s(%s) { [native code] }' % (this.name if this.name else u'', args) - + return u'function %s(%s) { [native code] }' % (this.name if this.name + else u'', args) + def call(this, args): if not is_callable(this): - raise MakeError('TypeError', 'Function.prototype.call is not generic') + raise MakeError('TypeError', + 'Function.prototype.call is not generic') _this = get_arg(args, 0) _args = tuple(args)[1:] return this.call(_this, _args) - + def apply(this, args): if not is_callable(this): - raise MakeError('TypeError', 'Function.prototype.apply is not generic') + raise MakeError('TypeError', + 'Function.prototype.apply is not generic') _args = get_arg(args, 1) if not is_object(_args): - raise MakeError('TypeError', 'argList argument to Function.prototype.apply must an Object') + raise MakeError( + 'TypeError', + 'argList argument to Function.prototype.apply must an Object') _this = get_arg(args, 0) return this.call(_this, js_array_to_tuple(_args)) def bind(this, args): if not is_callable(this): - raise MakeError('TypeError', 'Function.prototype.bind is not generic') + raise MakeError('TypeError', + 'Function.prototype.bind is not generic') bound_this = get_arg(args, 0) bound_args = tuple(args)[1:] + def bound(dummy_this, extra_args): return this.call(bound_this, bound_args + tuple(extra_args)) + js_bound = args.space.NewFunction(bound, this.ctx, (), u'', False, ()) - js_bound.put(u'length', float(max(len(this.params) - len(bound_args), 0.))) + js_bound.put(u'length', + float(max(len(this.params) - len(bound_args), 0.))) js_bound.put(u'name', u'boundFunc') return js_bound - - - diff --git a/js2py/internals/prototypes/jsjson.py b/js2py/internals/prototypes/jsjson.py index 62aa0457..87dc5f38 100644 --- a/js2py/internals/prototypes/jsjson.py +++ b/js2py/internals/prototypes/jsjson.py @@ -20,7 +20,9 @@ def parse(this, args): try: unfiltered = json.loads(s) except: - raise MakeError('SyntaxError', 'JSON.parse could not parse JSON string - Invalid syntax') + raise MakeError( + 'SyntaxError', + 'JSON.parse could not parse JSON string - Invalid syntax') unfiltered = to_js(unfiltered, args.space) if is_callable(reviver): root = args.space.ConstructObject({'': unfiltered}) @@ -31,42 +33,44 @@ def parse(this, args): def stringify(this, args): global indent - value, replacer, space = get_arg(args, 0), get_arg(args, 1), get_arg(args, 2) + value, replacer, space = get_arg(args, 0), get_arg(args, 1), get_arg( + args, 2) stack = set([]) indent = '' property_list = replacer_function = undefined if is_object(replacer): if is_callable(replacer): replacer_function = replacer - elif replacer.Class=='Array': + elif replacer.Class == 'Array': property_list = [] for e in replacer: v = replacer[e] item = undefined typ = Type(v) - if typ=='Number': + if typ == 'Number': item = to_string(v) - elif typ=='String': + elif typ == 'String': item = v - elif typ=='Object': + elif typ == 'Object': if GetClass(v) in ('String', 'Number'): item = to_string(v) if not is_undefined(item) and item not in property_list: property_list.append(item) if is_object(space): - if GetClass(space)=='Number': + if GetClass(space) == 'Number': space = to_number(space) - elif GetClass(space)=='String': + elif GetClass(space) == 'String': space = to_string(space) - if Type(space)=='Number': - space =min(10, to_int(space)) - gap = max(int(space), 0)* ' ' - elif Type(space)=='String': + if Type(space) == 'Number': + space = min(10, to_int(space)) + gap = max(int(space), 0) * ' ' + elif Type(space) == 'String': gap = space[:10] else: gap = '' - return Str('', args.space.ConstructObject({'':value}), replacer_function, property_list, gap, stack, space) - + return Str('', args.space.ConstructObject({ + '': value + }), replacer_function, property_list, gap, stack, space) def Str(key, holder, replacer_function, property_list, gap, stack, space): @@ -74,37 +78,38 @@ def Str(key, holder, replacer_function, property_list, gap, stack, space): if is_object(value): to_json = value.get('toJSON') if is_callable(to_json): - value = to_json.call(value, (key,)) + value = to_json.call(value, (key, )) if not is_undefined(replacer_function): value = replacer_function.call(holder, (key, value)) if is_object(value): - if value.Class=='String': + if value.Class == 'String': value = to_string(value) - elif value.Class=='Number': + elif value.Class == 'Number': value = to_number(value) - elif value.Class=='Boolean': + elif value.Class == 'Boolean': value = to_boolean(value) typ = Type(value) if is_null(value): return 'null' - elif typ=='Boolean': + elif typ == 'Boolean': return 'true' if value else 'false' - elif typ=='String': + elif typ == 'String': return Quote(value) - elif typ=='Number': + elif typ == 'Number': if not is_infinity(value): return to_string(value) return 'null' if is_object(value) and not is_callable(value): - if value.Class=='Array': - return ja(value, stack, gap, property_list, replacer_function, space) + if value.Class == 'Array': + return ja(value, stack, gap, property_list, replacer_function, + space) else: - return jo(value, stack, gap, property_list, replacer_function, space) + return jo(value, stack, gap, property_list, replacer_function, + space) return undefined - def jo(value, stack, gap, property_list, replacer_function, space): global indent if value in stack: @@ -115,12 +120,15 @@ def jo(value, stack, gap, property_list, replacer_function, space): if not is_undefined(property_list): k = property_list else: - k = [unicode(e) for e,d in value.own.items() if d.get('enumerable')] + k = [unicode(e) for e, d in value.own.items() if d.get('enumerable')] partial = [] for p in k: - str_p = Str(p, value, replacer_function, property_list, gap, stack, space) + str_p = Str(p, value, replacer_function, property_list, gap, stack, + space) if not is_undefined(str_p): - member = json.dumps(p) + ':' + (' ' if gap else '') + str_p # todo not sure here - what space character? + member = json.dumps(p) + ':' + ( + ' ' if gap else + '') + str_p # todo not sure here - what space character? partial.append(member) if not partial: final = '{}' @@ -128,9 +136,9 @@ def jo(value, stack, gap, property_list, replacer_function, space): if not gap: final = '{%s}' % ','.join(partial) else: - sep = ',\n'+indent + sep = ',\n' + indent properties = sep.join(partial) - final = '{\n'+indent+properties+'\n'+stepback+'}' + final = '{\n' + indent + properties + '\n' + stepback + '}' stack.remove(value) indent = stepback return final @@ -147,7 +155,8 @@ def ja(value, stack, gap, property_list, replacer_function, space): length = js_arr_length(value) for index in xrange(length): index = unicode(index) - str_index = Str(index, value, replacer_function, property_list, gap, stack, space) + str_index = Str(index, value, replacer_function, property_list, gap, + stack, space) if is_undefined(str_index): partial.append('null') else: @@ -158,17 +167,14 @@ def ja(value, stack, gap, property_list, replacer_function, space): if not gap: final = '[%s]' % ','.join(partial) else: - sep = ',\n'+indent + sep = ',\n' + indent properties = sep.join(partial) - final = '[\n'+indent +properties+'\n'+stepback+']' + final = '[\n' + indent + properties + '\n' + stepback + ']' stack.remove(value) indent = stepback return final - - - def Quote(string): return json.dumps(string) @@ -179,7 +185,7 @@ def to_js(d, _args_space): def walk(holder, name, reviver): val = holder.get(name) - if GetClass(val)=='Array': + if GetClass(val) == 'Array': for i in xrange(js_arr_length(val)): i = unicode(i) new_element = walk(val, i, reviver) @@ -188,15 +194,12 @@ def walk(holder, name, reviver): else: new_element.put(i, new_element) elif is_object(val): - for key in [unicode(e) for e,d in val.own.items() if d.get('enumerable')]: + for key in [ + unicode(e) for e, d in val.own.items() if d.get('enumerable') + ]: new_element = walk(val, key, reviver) if is_undefined(new_element): val.delete(key) else: val.put(key, new_element) return reviver.call(holder, (name, val)) - - - - - diff --git a/js2py/internals/prototypes/jsnumber.py b/js2py/internals/prototypes/jsnumber.py index 05d1fa2e..d099eabd 100644 --- a/js2py/internals/prototypes/jsnumber.py +++ b/js2py/internals/prototypes/jsnumber.py @@ -9,38 +9,75 @@ xrange = range unicode = str - -RADIX_SYMBOLS = {0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', - 10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f', 16: 'g', 17: 'h', 18: 'i', 19: 'j', 20: 'k', - 21: 'l', 22: 'm', 23: 'n', 24: 'o', 25: 'p', 26: 'q', 27: 'r', 28: 's', 29: 't', 30: 'u', 31: 'v', - 32: 'w', 33: 'x', 34: 'y', 35: 'z'} +RADIX_SYMBOLS = { + 0: '0', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: 'a', + 11: 'b', + 12: 'c', + 13: 'd', + 14: 'e', + 15: 'f', + 16: 'g', + 17: 'h', + 18: 'i', + 19: 'j', + 20: 'k', + 21: 'l', + 22: 'm', + 23: 'n', + 24: 'o', + 25: 'p', + 26: 'q', + 27: 'r', + 28: 's', + 29: 't', + 30: 'u', + 31: 'v', + 32: 'w', + 33: 'x', + 34: 'y', + 35: 'z' +} def to_str_rep(num): if is_nan(num): return 'NaN' elif is_infinity(num): - sign = '-' if num<0 else '' - return sign+'Infinity' - elif int(num)==num: # dont print .0 + sign = '-' if num < 0 else '' + return sign + 'Infinity' + elif int(num) == num: # dont print .0 return unicode(int(num)) - return unicode(num) # todo: Make it 100% consistent with Node + return unicode(num) # todo: Make it 100% consistent with Node class NumberPrototype: def toString(this, args): - if GetClass(this)!='Number': - raise MakeError('TypeError', 'Number.prototype.valueOf is not generic') - if type(this)!= float: + if GetClass(this) != 'Number': + raise MakeError('TypeError', + 'Number.prototype.valueOf is not generic') + if type(this) != float: this = this.value radix = get_arg(args, 0) if is_undefined(radix): return to_str_rep(this) r = to_int(radix) - if r==10: + if r == 10: return to_str_rep(this) - if r not in xrange(2, 37) or radix!=r: - raise MakeError('RangeError', 'Number.prototype.toString() radix argument must be an integer between 2 and 36') + if r not in xrange(2, 37) or radix != r: + raise MakeError( + 'RangeError', + 'Number.prototype.toString() radix argument must be an integer between 2 and 36' + ) num = to_int(this) if num < 0: num = -num @@ -55,47 +92,58 @@ def toString(this, args): return sign + (res if res else '0') def valueOf(this, args): - if GetClass(this)!='Number': - raise MakeError('TypeError', 'Number.prototype.valueOf is not generic') - if type(this)!=float: + if GetClass(this) != 'Number': + raise MakeError('TypeError', + 'Number.prototype.valueOf is not generic') + if type(this) != float: this = this.value return this - def toFixed (this, args): - if GetClass(this)!='Number': - raise MakeError('TypeError', 'Number.prototype.toFixed called on incompatible receiver') - if type(this)!= float: + def toFixed(this, args): + if GetClass(this) != 'Number': + raise MakeError( + 'TypeError', + 'Number.prototype.toFixed called on incompatible receiver') + if type(this) != float: this = this.value fractionDigits = get_arg(args, 0) digs = to_int(fractionDigits) - if digs<0 or digs>20: - raise MakeError('RangeError', 'toFixed() digits argument must be between 0 and 20') + if digs < 0 or digs > 20: + raise MakeError( + 'RangeError', + 'toFixed() digits argument must be between 0 and 20') elif is_infinity(this): - return 'Infinity' if this>0 else '-Infinity' + return 'Infinity' if this > 0 else '-Infinity' elif is_nan(this): return 'NaN' - return format(this, '-.%df'%digs) - + return format(this, '-.%df' % digs) def toExponential(this, args): - if GetClass(this)!='Number': - raise MakeError('TypeError', 'Number.prototype.toExponential called on incompatible receiver') - if type(this)!= float: + if GetClass(this) != 'Number': + raise MakeError( + 'TypeError', + 'Number.prototype.toExponential called on incompatible receiver' + ) + if type(this) != float: this = this.value fractionDigits = get_arg(args, 0) digs = to_int(fractionDigits) - if digs<0 or digs>20: - raise MakeError('RangeError', 'toFixed() digits argument must be between 0 and 20') + if digs < 0 or digs > 20: + raise MakeError( + 'RangeError', + 'toFixed() digits argument must be between 0 and 20') elif is_infinity(this): - return 'Infinity' if this>0 else '-Infinity' + return 'Infinity' if this > 0 else '-Infinity' elif is_nan(this): return 'NaN' - return format(this, '-.%de'%digs) + return format(this, '-.%de' % digs) - def toPrecision (this, args): - if GetClass(this)!='Number': - raise MakeError('TypeError', 'Number.prototype.toPrecision called on incompatible receiver') - if type(this)!= float: + def toPrecision(this, args): + if GetClass(this) != 'Number': + raise MakeError( + 'TypeError', + 'Number.prototype.toPrecision called on incompatible receiver') + if type(this) != float: this = this.value precision = get_arg(args, 0) if is_undefined(precision): @@ -104,12 +152,12 @@ def toPrecision (this, args): if is_nan(this): return 'NaN' elif is_infinity(this): - return 'Infinity' if this>0 else '-Infinity' + return 'Infinity' if this > 0 else '-Infinity' digs = prec - len(str(int(this))) - if digs>=0: - return format(this, '-.%df'%digs) + if digs >= 0: + return format(this, '-.%df' % digs) else: - return format(this, '-.%df'%(prec-1)) + return format(this, '-.%df' % (prec - 1)) -NumberPrototype.toLocaleString = NumberPrototype.toString +NumberPrototype.toLocaleString = NumberPrototype.toString diff --git a/js2py/internals/prototypes/jsobject.py b/js2py/internals/prototypes/jsobject.py index b7af780f..cdda6bab 100644 --- a/js2py/internals/prototypes/jsobject.py +++ b/js2py/internals/prototypes/jsobject.py @@ -5,11 +5,11 @@ class ObjectPrototype: def toString(this, args): - if type(this)==UNDEFINED_TYPE: + if type(this) == UNDEFINED_TYPE: return u'[object Undefined]' - elif type(this)==NULL_TYPE: + elif type(this) == NULL_TYPE: return u'[object Null]' - return u'[object %s]'% GetClass(to_object(this, args.space)) + return u'[object %s]' % GetClass(to_object(this, args.space)) def valueOf(this, args): return to_object(this, args.space) @@ -46,9 +46,3 @@ def propertyIsEnumerable(this, args): o = to_object(this, args.space) cand = o.own.get(to_string(prop)) return cand is not None and cand.get('enumerable') - - - - - - diff --git a/js2py/internals/prototypes/jsregexp.py b/js2py/internals/prototypes/jsregexp.py index 827740e8..54aba327 100644 --- a/js2py/internals/prototypes/jsregexp.py +++ b/js2py/internals/prototypes/jsregexp.py @@ -3,6 +3,7 @@ from ..conversions import * from ..func_utils import * + class RegExpPrototype: def toString(this, args): flags = u'' @@ -19,21 +20,21 @@ def toString(this, args): v = this.value if this.value else u'(?:)' except: v = u'(?:)' - return u'/%s/'%v + flags + return u'/%s/' % v + flags def test(this, args): string = get_arg(args, 0) return RegExpExec(this, string, args.space) is not null - def _exec(this, args): # will be changed to exec in base.py. cant name it exec here... + def _exec( + this, args + ): # will be changed to exec in base.py. cant name it exec here... string = get_arg(args, 0) return RegExpExec(this, string, args.space) - - def RegExpExec(this, string, space): - if GetClass(this)!='RegExp': + if GetClass(this) != 'RegExp': raise MakeError('TypeError', 'RegExp.prototype.exec is not generic!') string = to_string(string) length = len(string) @@ -45,11 +46,11 @@ def RegExpExec(this, string, space): return null matched = this.match(string, i) i += 1 - start, end = matched.span()#[0]+i-1, matched.span()[1]+i-1 + start, end = matched.span() #[0]+i-1, matched.span()[1]+i-1 if this.glob: this.put('lastIndex', float(end)) - arr = convert_to_js_type([matched.group()]+list(matched.groups()), space=space) + arr = convert_to_js_type( + [matched.group()] + list(matched.groups()), space=space) arr.put('index', float(start)) arr.put('input', unicode(string)) return arr - diff --git a/js2py/internals/prototypes/jsstring.py b/js2py/internals/prototypes/jsstring.py index b47fb669..b56246e2 100644 --- a/js2py/internals/prototypes/jsstring.py +++ b/js2py/internals/prototypes/jsstring.py @@ -9,63 +9,67 @@ 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: + while n < len(rep) - 1: char = rep[n] - if char=='$': - if rep[n+1]=='$': + if char == '$': + if rep[n + 1] == '$': res += '$' n += 2 continue - elif rep[n+1]=='`': + elif rep[n + 1] == '`': # replace with string that is BEFORE match res += source[:span[0]] n += 2 continue - elif rep[n+1]=='\'': + 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+2len(npar): - res += '$'+dig + 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 '' + res += npar[num - 1] if npar[num - 1] else '' n += 1 + len(dig) continue res += char n += 1 - if nthat: + elif s > that: return 1. return 0. @@ -119,7 +123,8 @@ 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 + 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)) @@ -129,11 +134,11 @@ def match(this, args): while last_match: result = RegExpExec(r, s, space=args.space) if is_null(result): - last_match=False + last_match = False else: this_index = r.get('lastIndex') - if this_index==previous_last_index: - r.put('lastIndex', float(this_index+1)) + if this_index == previous_last_index: + r.put('lastIndex', float(this_index + 1)) previous_last_index += 1 else: previous_last_index = this_index @@ -143,7 +148,6 @@ def match(this, args): return null return args.space.ConstructArray(found) - def replace(this, args): # VERY COMPLICATED. to check again. cok(this) @@ -163,15 +167,19 @@ def replace(this, args): res += s[last:e.span()[0]] if func: # prepare arguments for custom func (replaceValue) - call_args = (e.group(),) + e.groups() + (e.span()[1], s) + 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))) + res += to_string( + replaceValue.call( + this, ensure_js_types(call_args, + space=args.space))) else: - res += replacement_template(replaceValue, s, e.span(), e.groups()) + res += replacement_template(replaceValue, s, e.span(), + e.groups()) last = e.span()[1] res += s[last:] return res - elif GetClass(searchValue)=='RegExp': + elif GetClass(searchValue) == 'RegExp': e = re.search(searchValue.pat, s) if e is None: return s @@ -180,16 +188,19 @@ def replace(this, args): match = e.group() else: match = to_string(searchValue) - ind = s.find(match) - if ind==-1: + 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) + 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))) + 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]:] @@ -199,7 +210,7 @@ def search(this, args): cok(this) string = to_string(this) regexp = get_arg(args, 0) - if GetClass(regexp)=='RegExp': + if GetClass(regexp) == 'RegExp': rx = regexp else: rx = args.space.NewRegExp(regexp, '') @@ -219,44 +230,43 @@ def slice(this, args): #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) + 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) + 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: + while q != len_s: e, cap = SplitMatch(s, q, R) - if e is None or e==p: + if e is None or e == p: q += 1 continue res.append(s[p:q]) p = q = e - if len(res)==lim: + if len(res) == lim: return args.space.ConstructArray(res) for element in cap: res.append(element) - if len(res)==lim: + if len(res) == lim: return args.space.ConstructArray(res) res.append(s[p:]) return args.space.ConstructArray(res) - - def substring (this, args): + def substring(this, args): cok(this) s = to_string(this) start = to_int(get_arg(args, 0)) @@ -271,15 +281,15 @@ 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) + 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: + 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] + return r1[r5:r5 + r6] def toLowerCase(this, args): cok(this) @@ -302,15 +312,12 @@ def trim(this, args): 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': + 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 q + len(R), () return None, () - diff --git a/js2py/internals/prototypes/jsutils.py b/js2py/internals/prototypes/jsutils.py index d35ddded..85f5bd44 100644 --- a/js2py/internals/prototypes/jsutils.py +++ b/js2py/internals/prototypes/jsutils.py @@ -2,51 +2,109 @@ from ..conversions import * from ..func_utils import * - -RADIX_CHARS = {'1': 1, '0': 0, '3': 3, '2': 2, '5': 5, '4': 4, '7': 7, '6': 6, '9': 9, '8': 8, 'a': 10, 'c': 12, - 'b': 11, 'e': 14, 'd': 13, 'g': 16, 'f': 15, 'i': 18, 'h': 17, 'k': 20, 'j': 19, 'm': 22, 'l': 21, - 'o': 24, 'n': 23, 'q': 26, 'p': 25, 's': 28, 'r': 27, 'u': 30, 't': 29, 'w': 32, 'v': 31, 'y': 34, - 'x': 33, 'z': 35, 'A': 10, 'C': 12, 'B': 11, 'E': 14, 'D': 13, 'G': 16, 'F': 15, 'I': 18, 'H': 17, - 'K': 20, 'J': 19, 'M': 22, 'L': 21, 'O': 24, 'N': 23, 'Q': 26, 'P': 25, 'S': 28, 'R': 27, 'U': 30, - 'T': 29, 'W': 32, 'V': 31, 'Y': 34, 'X': 33, 'Z': 35} +RADIX_CHARS = { + '1': 1, + '0': 0, + '3': 3, + '2': 2, + '5': 5, + '4': 4, + '7': 7, + '6': 6, + '9': 9, + '8': 8, + 'a': 10, + 'c': 12, + 'b': 11, + 'e': 14, + 'd': 13, + 'g': 16, + 'f': 15, + 'i': 18, + 'h': 17, + 'k': 20, + 'j': 19, + 'm': 22, + 'l': 21, + 'o': 24, + 'n': 23, + 'q': 26, + 'p': 25, + 's': 28, + 'r': 27, + 'u': 30, + 't': 29, + 'w': 32, + 'v': 31, + 'y': 34, + 'x': 33, + 'z': 35, + 'A': 10, + 'C': 12, + 'B': 11, + 'E': 14, + 'D': 13, + 'G': 16, + 'F': 15, + 'I': 18, + 'H': 17, + 'K': 20, + 'J': 19, + 'M': 22, + 'L': 21, + 'O': 24, + 'N': 23, + 'Q': 26, + 'P': 25, + 'S': 28, + 'R': 27, + 'U': 30, + 'T': 29, + 'W': 32, + 'V': 31, + 'Y': 34, + 'X': 33, + 'Z': 35 +} # parseFloat # parseInt # isFinite # isNaN + def parseInt(this, args): string, radix = get_arg(args, 0), get_arg(args, 1) string = to_string(string).lstrip() sign = 1 if string and string[0] in ('+', '-'): - if string[0]=='-': + if string[0] == '-': sign = -1 string = string[1:] r = to_int32(radix) strip_prefix = True if r: - if r<2 or r>36: + if r < 2 or r > 36: return NaN - if r!=16: + if r != 16: strip_prefix = False else: r = 10 if strip_prefix: - if len(string)>=2 and string[:2] in ('0x', '0X'): + if len(string) >= 2 and string[:2] in ('0x', '0X'): string = string[2:] r = 16 n = 0 - num = 0 - while n4: # cant be a number anymore + if failed > 4: # cant be a number anymore break length += 1 if num is None: return NaN - return sign*float(string[:max_len]) + return sign * float(string[:max_len]) def isNaN(this, args): @@ -88,4 +146,4 @@ def isFinite(this, args): num = to_number(number) if is_nan(num) or is_infinity(num): return False - return True \ No newline at end of file + return True diff --git a/js2py/internals/seval.py b/js2py/internals/seval.py index 2b1430b3..a3ed39c0 100644 --- a/js2py/internals/seval.py +++ b/js2py/internals/seval.py @@ -10,6 +10,7 @@ pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError(u'SyntaxError', unicode(msg)) + def eval_js_vm(js): a = ByteCodeGenerator(Code()) s = Space() @@ -24,5 +25,3 @@ def eval_js_vm(js): a.exe.compile() return a.exe.run(a.exe.space.GlobalObj) - - diff --git a/js2py/internals/simplex.py b/js2py/internals/simplex.py index 916611dc..b05f6174 100644 --- a/js2py/internals/simplex.py +++ b/js2py/internals/simplex.py @@ -1,15 +1,22 @@ from __future__ import unicode_literals import six + + #Undefined class PyJsUndefined(object): TYPE = 'Undefined' Class = 'Undefined' + + undefined = PyJsUndefined() + #Null class PyJsNull(object): TYPE = 'Null' Class = 'Null' + + null = PyJsNull() Infinity = float('inf') @@ -22,7 +29,8 @@ class PyJsNull(object): BOOLEAN_TYPE = bool # exactly 5 simplexes! -PRIMITIVES = frozenset([UNDEFINED_TYPE, NULL_TYPE, STRING_TYPE, NUMBER_TYPE, BOOLEAN_TYPE]) +PRIMITIVES = frozenset( + [UNDEFINED_TYPE, NULL_TYPE, STRING_TYPE, NUMBER_TYPE, BOOLEAN_TYPE]) TYPE_NAMES = { UNDEFINED_TYPE: 'Undefined', @@ -32,10 +40,12 @@ class PyJsNull(object): BOOLEAN_TYPE: 'Boolean', } + def Type(x): # Any -> Str return TYPE_NAMES.get(type(x), 'Object') + def GetClass(x): # Any -> Str cand = TYPE_NAMES.get(type(x)) @@ -43,27 +53,35 @@ def GetClass(x): return x.Class return cand + def is_undefined(self): return self is undefined + def is_null(self): return self is null + def is_primitive(self): return type(self) in PRIMITIVES + def is_object(self): return not is_primitive(self) + def is_callable(self): return hasattr(self, 'call') + def is_infinity(self): return self == float('inf') or self == -float('inf') + def is_nan(self): return self != self # nan!=nan evaluates to True + def is_finite(self): return not (is_nan(self) or is_infinity(self)) @@ -74,7 +92,9 @@ def __init__(self, typ=None, message=None, throw=None): # it means its the trasnlator based error (old format), do nothing self._translator_based = True else: - assert throw is None or (typ is None and message is None), (throw, typ, message) + assert throw is None or (typ is None + and message is None), (throw, typ, + message) self._translator_based = False self.typ = typ self.message = message @@ -97,12 +117,14 @@ def __str__(self): from conversions import to_string return to_string(self.throw) else: - return self.typ+': '+self.message - + return self.typ + ': ' + self.message def MakeError(typ, message=u'no info', throw=None): - return JsException(typ, unicode(message) if message is not None else message, throw) + return JsException(typ, + unicode(message) if message is not None else message, + throw) + def value_from_js_exception(js_exception, space): if js_exception.throw is not None: diff --git a/js2py/internals/space.py b/js2py/internals/space.py index f6e267a3..7283070c 100644 --- a/js2py/internals/space.py +++ b/js2py/internals/space.py @@ -1,6 +1,7 @@ from base import * from simplex import * + class Space(object): def __init__(self): self.GlobalObj = None @@ -47,16 +48,23 @@ def ERROR_TYPES(self): 'URIError': self.URIErrorPrototype, } - - def get_global_environment(self): return self.GlobalCtx.variable_environment() def NewObject(self): return PyJsObject(self.ObjectPrototype) - def NewFunction(self, code, ctx, params, name, is_declaration, definitions): - return PyJsFunction(code, ctx, params, name, self, is_declaration, definitions, prototype=self.FunctionPrototype) + def NewFunction(self, code, ctx, params, name, is_declaration, + definitions): + return PyJsFunction( + code, + ctx, + params, + name, + self, + is_declaration, + definitions, + prototype=self.FunctionPrototype) def NewDate(self, value): return PyJsDate(value, self.DatePrototype) diff --git a/js2py/internals/speed.py b/js2py/internals/speed.py index 82f9e62a..3ca4079e 100644 --- a/js2py/internals/speed.py +++ b/js2py/internals/speed.py @@ -4,14 +4,17 @@ from itertools import izip from collections import deque + class Y(object): UUU = 88 + def __init__(self, x): self.x = x def s(self, x): return self.x + 1 + class X(Y): A = 10 B = 2 @@ -29,32 +32,31 @@ def s(self, x): def __add__(self, other): return self.x + other.x - def another(self): return Y.s(self, 1) def yet_another(self): return self.par.s(1) -def add(a,b): - return a.x + b.x - +def add(a, b): + return a.x + b.x t = [] - Type = None try: print timeit( -""" + """ t.append(4) t.pop() -""", "from __main__ import X,Y,namedtuple,array,t,add,Type, izip", number=1000000) +""", + "from __main__ import X,Y,namedtuple,array,t,add,Type, izip", + number=1000000) except: - raise \ No newline at end of file + raise diff --git a/js2py/internals/trans_utils.py b/js2py/internals/trans_utils.py index 298fb9c9..235b46a8 100644 --- a/js2py/internals/trans_utils.py +++ b/js2py/internals/trans_utils.py @@ -1,4 +1,3 @@ - def to_key(literal_or_identifier): ''' returns string representation of this object''' if literal_or_identifier['type'] == 'Identifier': @@ -26,4 +25,4 @@ def compose_regex(val): def float_repr(f): if int(f) == f: return unicode(repr(int(f))) - return unicode(repr(f)) \ No newline at end of file + return unicode(repr(f)) diff --git a/js2py/legecy_translators/constants.py b/js2py/legecy_translators/constants.py index 96bb79fe..ea048f12 100644 --- a/js2py/legecy_translators/constants.py +++ b/js2py/legecy_translators/constants.py @@ -4,48 +4,51 @@ NumberName = u'PyJsConstantNumber%d_' RegExpName = u'PyJsConstantRegExp%d_' ################################## -ALPHAS = set(ascii_lowercase+ ascii_lowercase.upper()) +ALPHAS = set(ascii_lowercase + ascii_lowercase.upper()) NUMS = set(digits) IDENTIFIER_START = ALPHAS.union(NUMS) ESCAPE_CHARS = {'n', '0', 'b', 'f', 'r', 't', 'v', '"', "'", '\\'} OCTAL = {'0', '1', '2', '3', '4', '5', '6', '7'} HEX = set('0123456789abcdefABCDEF') from utils import * -IDENTIFIER_PART = IDENTIFIER_PART.union({'.'}) +IDENTIFIER_PART = IDENTIFIER_PART.union({'.'}) def _is_cancelled(source, n): cancelled = False k = 0 while True: - k+=1 - if source[n-k]!='\\': + k += 1 + if source[n - k] != '\\': break cancelled = not cancelled return cancelled -def _ensure_regexp(source, n): #<- this function has to be improved + +def _ensure_regexp(source, n): #<- this function has to be improved '''returns True if regexp starts at n else returns False checks whether it is not a division ''' markers = '(+~"\'=[%:?!*^|&-,;/\\' k = 0 while True: - k+=1 - if n-k<0: + k += 1 + if n - k < 0: return True - char = source[n-k] + char = source[n - k] if char in markers: return True - if char!=' ' and char!='\n': + if char != ' ' and char != '\n': break return False + def parse_num(source, start, charset): """Returns a first index>=start of chat not in charset""" - while start max_num: break num = cand @@ -278,17 +295,14 @@ def do_escape(source, n): # we have to return in a different form because python may want to parse more... # for example '\777' will be parsed by python as a whole while js will use only \77 return '\\' + hex(num)[1:], n + len_parsed - return source[n+1], n+2 - - - + return source[n + 1], n + 2 #####TEST###### -if __name__=='__main__': +if __name__ == '__main__': test = (''' ''') t, d = remove_constants(test) - print t, d \ No newline at end of file + print t, d diff --git a/js2py/legecy_translators/exps.py b/js2py/legecy_translators/exps.py index 5e72abca..3c8f793c 100644 --- a/js2py/legecy_translators/exps.py +++ b/js2py/legecy_translators/exps.py @@ -19,6 +19,7 @@ from utils import * from jsparser import * + def exps_translator(js): #Check () {} and [] nums ass = assignment_translator(js) @@ -29,7 +30,7 @@ def assignment_translator(js): sep = js.split(',') res = sep[:] for i, e in enumerate(sep): - if '=' not in e: # no need to convert + if '=' not in e: # no need to convert continue res[i] = bass_translator(e) return ','.join(res) @@ -39,11 +40,11 @@ def bass_translator(s): # I hope that I will not have to fix any bugs here because it will be terrible if '(' in s or '[' in s: converted = '' - for e in bracket_split(s, ['()','[]'], strip=False): - if e[0]=='(': - converted += '(' + bass_translator(e[1:-1])+')' - elif e[0]=='[': - converted += '[' + bass_translator(e[1:-1])+']' + for e in bracket_split(s, ['()', '[]'], strip=False): + if e[0] == '(': + converted += '(' + bass_translator(e[1:-1]) + ')' + elif e[0] == '[': + converted += '[' + bass_translator(e[1:-1]) + ']' else: converted += e s = converted @@ -54,26 +55,29 @@ def bass_translator(s): res = last for e in ass: op = '' - if e[-1] in OP_METHODS: #increment assign like += - op = ', "'+e[-1]+'"' + if e[-1] in OP_METHODS: #increment assign like += + op = ', "' + e[-1] + '"' e = e[:-1] - cand = e.strip('() ') # (a) = 40 is valid so we need to transform '(a) ' to 'a' - if not is_property_accessor(cand): # it is not a property assignment + cand = e.strip( + '() ') # (a) = 40 is valid so we need to transform '(a) ' to 'a' + if not is_property_accessor(cand): # it is not a property assignment if not is_lval(cand) or is_internal(cand): raise SyntaxError('Invalid left-hand side in assignment') - res = 'var.put(%s, %s%s)'%(cand.__repr__(), res, op) - elif cand[-1]==']': # property assignment via [] + res = 'var.put(%s, %s%s)' % (cand.__repr__(), res, op) + elif cand[-1] == ']': # property assignment via [] c = list(bracket_split(cand, ['[]'], strip=False)) - meth, prop = ''.join(c[:-1]).strip(), c[-1][1:-1].strip() #this does not have to be a string so dont remove - #() because it can be a call - res = '%s.put(%s, %s%s)'%(meth, prop, res, op) + meth, prop = ''.join(c[:-1]).strip(), c[-1][1:-1].strip( + ) #this does not have to be a string so dont remove + #() because it can be a call + res = '%s.put(%s, %s%s)' % (meth, prop, res, op) else: # Prop set via '.' c = cand.rfind('.') - meth, prop = cand[:c].strip(), cand[c+1:].strip('() ') + meth, prop = cand[:c].strip(), cand[c + 1:].strip('() ') if not is_lval(prop): raise SyntaxError('Invalid left-hand side in assignment') - res = '%s.put(%s, %s%s)'%(meth, prop.__repr__(), res, op) + res = '%s.put(%s, %s%s)' % (meth, prop.__repr__(), res, op) return res -if __name__=='__main__': - print bass_translator('3.ddsd = 40') \ No newline at end of file + +if __name__ == '__main__': + print bass_translator('3.ddsd = 40') diff --git a/js2py/legecy_translators/flow.py b/js2py/legecy_translators/flow.py index 523859cb..9e2bb0fa 100644 --- a/js2py/legecy_translators/flow.py +++ b/js2py/legecy_translators/flow.py @@ -14,26 +14,27 @@ from nodevisitor import exp_translator import random - TO_REGISTER = [] CONTINUE_LABEL = 'JS_CONTINUE_LABEL_%s' BREAK_LABEL = 'JS_BREAK_LABEL_%s' - PREPARE = '''HOLDER = var.own.get(NAME)\nvar.force_own_put(NAME, PyExceptionToJs(PyJsTempException))\n''' RESTORE = '''if HOLDER is not None:\n var.own[NAME] = HOLDER\nelse:\n del var.own[NAME]\ndel HOLDER\n''' TRY_CATCH = '''%stry:\nBLOCKfinally:\n%s''' % (PREPARE, indent(RESTORE)) + def get_continue_label(label): - return CONTINUE_LABEL%label.encode('hex') + return CONTINUE_LABEL % label.encode('hex') + def get_break_label(label): - return BREAK_LABEL%label.encode('hex') + return BREAK_LABEL % label.encode('hex') + -def pass_until(source, start, tokens=(';',)): +def pass_until(source, start, tokens=(';', )): while start < len(source) and source[start] not in tokens: - start+=1 - return start+1 + start += 1 + return start + 1 def do_bracket_exp(source, start, throw=True): @@ -47,12 +48,12 @@ def do_bracket_exp(source, start, throw=True): def do_if(source, start): - start += 2 # pass this if + start += 2 # pass this if bra, start = do_bracket_exp(source, start, throw=True) statement, start = do_statement(source, start) if statement is None: raise SyntaxError('Invalid if statement') - translated = 'if %s:\n'%bra+indent(statement) + translated = 'if %s:\n' % bra + indent(statement) elseif = except_keyword(source, start, 'else') is_elseif = False @@ -66,7 +67,7 @@ def do_if(source, start): if is_elseif: translated += 'el' + elseif else: - translated += 'else:\n'+ indent(elseif) + translated += 'else:\n' + indent(elseif) return translated, start @@ -75,39 +76,40 @@ def do_statement(source, start): also this do_ type function passes white space""" start = pass_white(source, start) # start is the fist position after initial start that is not a white space or \n - if not start < len(source): #if finished parsing return None + if not start < len(source): #if finished parsing return None return None, start if any(startswith_keyword(source[start:], e) for e in {'case', 'default'}): return None, start rest = source[start:] - for key, meth in KEYWORD_METHODS.iteritems(): # check for statements that are uniquely defined by their keywords + for key, meth in KEYWORD_METHODS.iteritems( + ): # check for statements that are uniquely defined by their keywords if rest.startswith(key): # has to startwith this keyword and the next letter after keyword must be either EOF or not in IDENTIFIER_PART - if len(key)==len(rest) or rest[len(key)] not in IDENTIFIER_PART: + if len(key) == len(rest) or rest[len(key)] not in IDENTIFIER_PART: return meth(source, start) - if rest[0] == '{': #Block + if rest[0] == '{': #Block return do_block(source, start) # Now only label and expression left cand = parse_identifier(source, start, False) - if cand is not None: # it can mean that its a label + if cand is not None: # it can mean that its a label label, cand_start = cand cand_start = pass_white(source, cand_start) - if source[cand_start]==':': + if source[cand_start] == ':': return do_label(source, start) return do_expression(source, start) def do_while(source, start): - start += 5 # pass while + start += 5 # pass while bra, start = do_bracket_exp(source, start, throw=True) statement, start = do_statement(source, start) if statement is None: raise SyntaxError('Missing statement to execute in while loop!') - return 'while %s:\n'%bra + indent(statement), start + return 'while %s:\n' % bra + indent(statement), start def do_dowhile(source, start): - start += 2 # pass do + start += 2 # pass do statement, start = do_statement(source, start) if statement is None: raise SyntaxError('Missing statement to execute in do while loop!') @@ -116,7 +118,7 @@ def do_dowhile(source, start): raise SyntaxError('Missing while keyword in do-while loop') bra, start = do_bracket_exp(source, start, throw=True) statement += 'if not %s:\n' % bra + indent('break\n') - return 'while 1:\n' + indent(statement), start + return 'while 1:\n' + indent(statement), start def do_block(source, start): @@ -126,25 +128,28 @@ def do_block(source, start): if bra is None: raise SyntaxError('Missing block ( {code} )') code = '' - bra = bra[1:-1]+';' + bra = bra[1:-1] + ';' bra_pos = 0 - while bra_pos=len(rev): + if rpos >= len(rev): raise if filter(lambda x: x not in SPACE, rev[lpos:rpos]): - break + break end = start + len(rev) - rpos + 1 - def do_var(source, start): #todo auto ; insertion - start += 3 #pass var - end = pass_until(source, start, tokens=(';',)) - defs = argsplit(source[start:end-1]) # defs is the list of defined vars with optional initializer + start += 3 #pass var + end = pass_until(source, start, tokens=(';', )) + defs = argsplit( + source[start:end - 1] + ) # defs is the list of defined vars with optional initializer code = '' for de in defs: var, var_end = parse_identifier(de, 0, True) TO_REGISTER.append(var) var_end = pass_white(de, var_end) - if var_end>': '__rshift__', - '&': '__and__', - '^': '__xor__', - '|': '__or__'} +OP_METHODS = { + '*': '__mul__', + '/': '__div__', + '%': '__mod__', + '+': '__add__', + '-': '__sub__', + '<<': '__lshift__', + '>>': '__rshift__', + '&': '__and__', + '^': '__xor__', + '|': '__or__' +} + def dbg(source): try: - with open('C:\Users\Piotrek\Desktop\dbg.py','w') as f: + with open('C:\Users\Piotrek\Desktop\dbg.py', 'w') as f: f.write(source) except: pass def indent(lines, ind=4): - return ind*' '+lines.replace('\n', '\n'+ind*' ').rstrip(' ') + return ind * ' ' + lines.replace('\n', '\n' + ind * ' ').rstrip(' ') def inject_before_lval(source, lval, code): - if source.count(lval)>1: + if source.count(lval) > 1: dbg(source) print print lval @@ -86,48 +89,49 @@ def inject_before_lval(source, lval, code): end = source.index(lval) inj = source.rfind('\n', 0, end) ind = inj - while source[ind+1]==' ': - ind+=1 + while source[ind + 1] == ' ': + ind += 1 ind -= inj - return source[:inj+1]+ indent(code, ind) + source[inj+1:] + return source[:inj + 1] + indent(code, ind) + source[inj + 1:] -def bracket_split(source, brackets=('()','{}','[]'), strip=False): +def bracket_split(source, brackets=('()', '{}', '[]'), strip=False): """DOES NOT RETURN EMPTY STRINGS (can only return empty bracket content if strip=True)""" starts = [e[0] for e in brackets] in_bracket = 0 n = 0 last = 0 - while n len(source): + if kl + start > len(source): return None - if source[start:start+kl] != keyword: + if source[start:start + kl] != keyword: return None - if kl+starttext_len: + if s + n > text_len: continue - if validitate and not validitate(e, text[:n], text[n+s:]): + if validitate and not validitate(e, text[:n], text[n + s:]): continue - if any(text[n+s:].startswith(e) for e in not_after): #Cant end with end before - n+=1 + if any(text[n + s:].startswith(e) + for e in not_after): #Cant end with end before + n += 1 break - if e==text[n:n+s]: - yield text[last:n] if not translate else translate(text[last:n]) + if e == text[n:n + s]: + yield text[last:n] if not translate else translate( + text[last:n]) yield e - n+=s + n += s last = n break else: - n+=1 + n += 1 yield text[last:n] if not translate else translate(text[last:n]) + def split_at_single(text, sep, not_before=[], not_after=[]): """Works like text.split(sep) but separated fragments cant end with not_before or start with not_after""" n = 0 - lt, s= len(text), len(sep) + lt, s = len(text), len(sep) last = 0 - while nlt: - if sep==text[n:n+s]: + while n < lt: + if not s + n > lt: + if sep == text[n:n + s]: if any(text[last:n].endswith(e) for e in not_before): pass - elif any(text[n+s:].startswith(e) for e in not_after): + elif any(text[n + s:].startswith(e) for e in not_after): pass else: yield text[last:n] - last = n+s - n += s-1 - n+=1 - yield text[last:] \ No newline at end of file + last = n + s + n += s - 1 + n += 1 + yield text[last:] diff --git a/js2py/legecy_translators/nodevisitor.py b/js2py/legecy_translators/nodevisitor.py index c642a2fb..4d50545c 100644 --- a/js2py/legecy_translators/nodevisitor.py +++ b/js2py/legecy_translators/nodevisitor.py @@ -6,43 +6,46 @@ #Note all white space sent to this module must be ' ' so no '\n' REPL = {} - #PROBLEMS # <<=, >>=, >>>= # they are unusual so I will not fix that now. a++ +b works fine and a+++++b (a++ + ++b) does not work even in V8 ASSIGNMENT_MATCH = '(?)=(?!=)' + def unary_validitator(keyword, before, after): if keyword[-1] in IDENTIFIER_PART: if not after or after[0] in IDENTIFIER_PART: return False - if before and before[-1] in IDENTIFIER_PART: # I am not sure here... + if before and before[-1] in IDENTIFIER_PART: # I am not sure here... return False return True + def comb_validitator(keyword, before, after): - if keyword=='instanceof' or keyword=='in': + if keyword == 'instanceof' or keyword == 'in': if before and before[-1] in IDENTIFIER_PART: return False elif after and after[0] in IDENTIFIER_PART: return False return True + def bracket_replace(code): new = '' - for e in bracket_split(code, ['()','[]'], False): - if e[0]=='[': - name = '#PYJSREPL'+str(len(REPL))+'{' - new+= name + for e in bracket_split(code, ['()', '[]'], False): + if e[0] == '[': + name = '#PYJSREPL' + str(len(REPL)) + '{' + new += name REPL[name] = e - elif e[0]=='(': # can be a function call - name = '@PYJSREPL'+str(len(REPL))+'}' - new+= name + elif e[0] == '(': # can be a function call + name = '@PYJSREPL' + str(len(REPL)) + '}' + new += name REPL[name] = e else: - new+=e + new += e return new + class NodeVisitor: def __init__(self, code): self.code = code @@ -76,8 +79,8 @@ def translate(self): return '' new = bracket_replace(self.code) #Check comma operator: - cand = new.split(',') #every comma in new must be an operator - if len(cand)>1: #LR + cand = new.split(',') #every comma in new must be an operator + if len(cand) > 1: #LR return self.lr(cand, js_comma) #Check = operator: # dont split at != or !== or == or === or <= or >= @@ -94,34 +97,36 @@ def translate(self): else: break actual_tenary = new[tenary_start:] - spl = ''.join(split_at_any(new, [':', '?'], translate=trans)) + spl = ''.join(split_at_any(new, [':', '?'], translate=trans)) tenary_translation = transform_crap(spl) assignment = new[:tenary_start] + ' PyJsConstantTENARY' - return trans(assignment).replace('PyJsConstantTENARY', tenary_translation) - cand = list(split_at_single(new, '=', ['!', '=','<','>'], ['='])) - if len(cand)>1: # RL + return trans(assignment).replace('PyJsConstantTENARY', + tenary_translation) + cand = list(split_at_single(new, '=', ['!', '=', '<', '>'], ['='])) + if len(cand) > 1: # RL it = reversed(cand) - res = trans(it.next()) + res = trans(it.next()) for e in it: e = e.strip() if not e: raise SyntaxError('Missing left-hand in assignment!') op = '' if e[-2:] in OP_METHODS: - op = ','+e[-2:].__repr__() + op = ',' + e[-2:].__repr__() e = e[:-2] elif e[-1:] in OP_METHODS: - op = ','+e[-1].__repr__() + op = ',' + e[-1].__repr__() e = e[:-1] e = trans(e) #Now replace last get method with put and change args c = list(bracket_split(e, ['()'])) - beg, arglist = ''.join(c[:-1]).strip(), c[-1].strip() #strips just to make sure... I will remove it later - if beg[-4:]!='.get': + beg, arglist = ''.join(c[:-1]).strip(), c[-1].strip( + ) #strips just to make sure... I will remove it later + if beg[-4:] != '.get': raise SyntaxError('Invalid left-hand side in assignment') - beg = beg[0:-3]+'put' - arglist = arglist[0:-1]+', '+res+op+')' - res = beg+arglist + beg = beg[0:-3] + 'put' + arglist = arglist[0:-1] + ', ' + res + op + ')' + res = beg + arglist return res #Now check remaining 2 arg operators that are not handled by python #They all have Left to Right (LR) associativity @@ -134,27 +139,36 @@ def translate(self): cand = list(split_add_ops(new)) else: #dont translate. cant start or end on dangerous op. - cand = list(split_at_any(new, typ.keys(), False, dangerous, dangerous,validitate=comb_validitator)) - if not len(cand)>1: + cand = list( + split_at_any( + new, + typ.keys(), + False, + dangerous, + dangerous, + validitate=comb_validitator)) + if not len(cand) > 1: continue n = 1 res = trans(cand[0]) if not res: raise SyntaxError("Missing operand!") - while n1: #contains unary operators - if '++' in cand or '--' in cand: #it cant contain both ++ and -- + cand = list( + split_at_any( + new, UNARY.keys(), False, validitate=unary_validitator)) + if len(cand) > 1: #contains unary operators + if '++' in cand or '--' in cand: #it cant contain both ++ and -- if '--' in cand: op = '--' meths = js_post_dec, js_pre_dec @@ -162,43 +176,46 @@ def translate(self): op = '++' meths = js_post_inc, js_pre_inc pos = cand.index(op) - if cand[pos-1].strip(): # post increment - a = cand[pos-1] + if cand[pos - 1].strip(): # post increment + a = cand[pos - 1] meth = meths[0] - elif cand[pos+1].strip(): #pre increment - a = cand[pos+1] + elif cand[pos + 1].strip(): #pre increment + a = cand[pos + 1] meth = meths[1] else: raise SyntaxError('Invalid use of ++ operator') - if cand[pos+2:]: + if cand[pos + 2:]: raise SyntaxError('Too many operands') operand = meth(trans(a)) - cand = cand[:pos-1] + cand = cand[:pos - 1] # now last cand should be operand and every other odd element should be empty else: operand = trans(cand[-1]) del cand[-1] for i, e in enumerate(reversed(cand)): - if i%2: + if i % 2: if e.strip(): raise SyntaxError('Too many operands') else: operand = UNARY[e](operand) return operand #Replace brackets - if new[0]=='@' or new[0]=='#': - if len(list(bracket_split(new, ('#{','@}')))) ==1: # we have only one bracket, otherwise pseudobracket like @@.... + if new[0] == '@' or new[0] == '#': + if len( + list(bracket_split(new, ('#{', '@}'))) + ) == 1: # we have only one bracket, otherwise pseudobracket like @@.... assert new in REPL - if new[0]=='#': - raise SyntaxError('[] cant be used as brackets! Use () instead.') - return '('+trans(REPL[new][1:-1])+')' + if new[0] == '#': + raise SyntaxError( + '[] cant be used as brackets! Use () instead.') + return '(' + trans(REPL[new][1:-1]) + ')' #Replace function calls and prop getters # 'now' must be a reference like: a or b.c.d but it can have also calls or getters ( for example a["b"](3)) #From here @@ means a function call and ## means get operation (note they dont have to present) - it = bracket_split(new, ('#{','@}')) + it = bracket_split(new, ('#{', '@}')) res = [] for e in it: - if e[0]!='#' and e[0]!='@': + if e[0] != '#' and e[0] != '@': res += [x.strip() for x in e.split('.')] else: res += [e.strip()] @@ -207,174 +224,210 @@ def translate(self): if is_internal(res[0]): out = res[0] elif res[0][0] in {'#', '@'}: - out = '('+trans(REPL[res[0]][1:-1])+')' - elif is_valid_lval(res[0]) or res[0] in {'this', 'false', 'true', 'null'}: - out = 'var.get('+res[0].__repr__()+')' + out = '(' + trans(REPL[res[0]][1:-1]) + ')' + elif is_valid_lval( + res[0]) or res[0] in {'this', 'false', 'true', 'null'}: + out = 'var.get(' + res[0].__repr__() + ')' else: if is_reserved(res[0]): - raise SyntaxError('Unexpected reserved word: "%s"'%res[0]) - raise SyntaxError('Invalid identifier: "%s"'%res[0]) - if len(res)==1: + raise SyntaxError('Unexpected reserved word: "%s"' % res[0]) + raise SyntaxError('Invalid identifier: "%s"' % res[0]) + if len(res) == 1: return out n = 1 - while n='+b+')' + return '(' + a + '>=' + b + ')' + def js_gt(a, b): - return '('+a+'>'+b+')' + return '(' + a + '>' + b + ')' + def js_in(a, b): - return b+'.contains('+a+')' + return b + '.contains(' + a + ')' + def js_instanceof(a, b): - return a+'.instanceof('+b+')' + return a + '.instanceof(' + b + ')' + def js_lshift(a, b): - return '('+a+'<<'+b+')' + return '(' + a + '<<' + b + ')' + def js_rshift(a, b): - return '('+a+'>>'+b+')' + return '(' + a + '>>' + b + ')' + def js_shit(a, b): - return 'PyJsBshift('+a+','+b+')' + return 'PyJsBshift(' + a + ',' + b + ')' + + +def js_add( + a, + b): # To simplify later process of converting unary operators + and ++ + return '(%s+%s)' % (a, b) -def js_add(a, b): # To simplify later process of converting unary operators + and ++ - return '(%s+%s)'%(a, b) def js_sub(a, b): # To simplify - return '(%s-%s)'%(a, b) + return '(%s-%s)' % (a, b) + def js_mul(a, b): - return '('+a+'*'+b+')' + return '(' + a + '*' + b + ')' + def js_div(a, b): - return '('+a+'/'+b+')' + return '(' + a + '/' + b + ')' + def js_mod(a, b): - return '('+a+'%'+b+')' + return '(' + a + '%' + b + ')' + def js_typeof(a): - cand = list(bracket_split(a, ('()',))) - if len(cand)==2 and cand[0]=='var.get': - return cand[0]+cand[1][:-1]+',throw=False).typeof()' - return a+'.typeof()' + cand = list(bracket_split(a, ('()', ))) + if len(cand) == 2 and cand[0] == 'var.get': + return cand[0] + cand[1][:-1] + ',throw=False).typeof()' + return a + '.typeof()' + def js_void(a): - return '('+a+')' + return '(' + a + ')' + def js_new(a): - cands = list(bracket_split(a, ('()',))) + cands = list(bracket_split(a, ('()', ))) lim = len(cands) if lim < 2: return a + '.create()' n = 0 while n < lim: c = cands[n] - if c[0]=='(': - if cands[n-1].endswith('.get') and n+1>=lim: # last get operation. + if c[0] == '(': + if cands[n - 1].endswith( + '.get') and n + 1 >= lim: # last get operation. return a + '.create()' - elif cands[n-1][0]=='(': - return ''.join(cands[:n])+'.create' + c + ''.join(cands[n+1:]) - elif cands[n-1]=='.callprop': - beg = ''.join(cands[:n-1]) - args = argsplit(c[1:-1],',') - prop = args[0] - new_args = ','.join(args[1:]) - create = '.get(%s).create(%s)' % (prop, new_args) - return beg + create + ''.join(cands[n+1:]) - n+=1 + elif cands[n - 1][0] == '(': + return ''.join(cands[:n]) + '.create' + c + ''.join( + cands[n + 1:]) + elif cands[n - 1] == '.callprop': + beg = ''.join(cands[:n - 1]) + args = argsplit(c[1:-1], ',') + prop = args[0] + new_args = ','.join(args[1:]) + create = '.get(%s).create(%s)' % (prop, new_args) + return beg + create + ''.join(cands[n + 1:]) + n += 1 return a + '.create()' - - - def js_delete(a): #replace last get with delete. c = list(bracket_split(a, ['()'])) - beg, arglist = ''.join(c[:-1]).strip(), c[-1].strip() #strips just to make sure... I will remove it later - if beg[-4:]!='.get': + beg, arglist = ''.join(c[:-1]).strip(), c[-1].strip( + ) #strips just to make sure... I will remove it later + if beg[-4:] != '.get': raise SyntaxError('Invalid delete operation') - return beg[:-3]+'delete'+arglist + return beg[:-3] + 'delete' + arglist + def js_neg(a): - return '(-'+a+')' + return '(-' + a + ')' + def js_pos(a): - return '(+'+a+')' + return '(+' + a + ')' + def js_inv(a): - return '(~'+a+')' + return '(~' + a + ')' + def js_not(a): - return a+'.neg()' + return a + '.neg()' + def postfix(a, inc, post): - bra = list(bracket_split(a, ('()',))) + bra = list(bracket_split(a, ('()', ))) meth = bra[-2] if not meth.endswith('get'): raise SyntaxError('Invalid ++ or -- operation.') @@ -383,15 +436,19 @@ def postfix(a, inc, post): res = ''.join(bra) return res if not post else '(%s%sJs(1))' % (res, '-' if inc else '+') + def js_pre_inc(a): return postfix(a, True, False) + def js_post_inc(a): return postfix(a, True, True) + def js_pre_dec(a): return postfix(a, False, False) + def js_post_dec(a): return postfix(a, False, True) @@ -402,65 +459,67 @@ def js_post_dec(a): BXOR = {'^': js_bxor} BAND = {'&': js_band} -EQS = {'===': js_strict_eq, - '!==': js_strict_neq, - '==': js_abstract_eq, # we need == and != too. Read a note above method - '!=': js_abstract_neq} +EQS = { + '===': js_strict_eq, + '!==': js_strict_neq, + '==': js_abstract_eq, # we need == and != too. Read a note above method + '!=': js_abstract_neq +} #Since JS does not have chained comparisons we need to implement all cmp methods. -COMPS = {'<': js_lt, - '<=': js_le, - '>=': js_ge, - '>': js_gt, - 'instanceof': js_instanceof, #todo change to validitate - 'in': js_in} - -BSHIFTS = {'<<': js_lshift, - '>>': js_rshift, - '>>>': js_shit} +COMPS = { + '<': js_lt, + '<=': js_le, + '>=': js_ge, + '>': js_gt, + 'instanceof': js_instanceof, #todo change to validitate + 'in': js_in +} -ADDS = {'+': js_add, - '-': js_sub} +BSHIFTS = {'<<': js_lshift, '>>': js_rshift, '>>>': js_shit} -MULTS = {'*': js_mul, - '/': js_div, - '%': js_mod} +ADDS = {'+': js_add, '-': js_sub} +MULTS = {'*': js_mul, '/': js_div, '%': js_mod} #Note they dont contain ++ and -- methods because they both have 2 different methods # correct method will be found automatically in translate function -UNARY = {'typeof': js_typeof, - 'void': js_void, - 'new': js_new, - 'delete': js_delete, - '!': js_not, - '-': js_neg, - '+': js_pos, - '~': js_inv, - '++': None, - '--': None - } - - -def transform_crap(code): #needs some more tests +UNARY = { + 'typeof': js_typeof, + 'void': js_void, + 'new': js_new, + 'delete': js_delete, + '!': js_not, + '-': js_neg, + '+': js_pos, + '~': js_inv, + '++': None, + '--': None +} + + +def transform_crap(code): #needs some more tests """Transforms this ?: crap into if else python syntax""" ind = code.rfind('?') - if ind==-1: + if ind == -1: return code sep = code.find(':', ind) - if sep==-1: + if sep == -1: raise SyntaxError('Invalid ?: syntax (probably missing ":" )') - beg = max(code.rfind(':', 0, ind), code.find('?', 0, ind))+1 - end = code.find(':',sep+1) - end = len(code) if end==-1 else end - formula = '('+code[ind+1:sep]+' if '+code[beg:ind]+' else '+code[sep+1:end]+')' - return transform_crap(code[:beg]+formula+code[end:]) + beg = max(code.rfind(':', 0, ind), code.find('?', 0, ind)) + 1 + end = code.find(':', sep + 1) + end = len(code) if end == -1 else end + formula = '(' + code[ind + 1:sep] + ' if ' + code[ + beg:ind] + ' else ' + code[sep + 1:end] + ')' + return transform_crap(code[:beg] + formula + code[end:]) from code import InteractiveConsole #e = InteractiveConsole(globals()).interact() import traceback + + def trans(code): return NodeVisitor(code.strip()).translate().strip() @@ -469,9 +528,12 @@ def trans(code): def trans_args(code): new = bracket_replace(code.strip()[1:-1]) args = ','.join(trans(e) for e in new.split(',')) - return '(%s)'%args + return '(%s)' % args + EXP = 0 + + def exp_translator(code): global REPL, EXP EXP += 1 @@ -490,11 +552,11 @@ def exp_translator(code): #raw_input('\n\npress enter') raise -if __name__=='__main__': + +if __name__ == '__main__': #print 'Here', trans('(eee ) . ii [ PyJsMarker ] [ jkj ] ( j , j ) . # jiji (h , ji , i)(non )( )()()()') for e in xrange(3): - print exp_translator('jk = kk.ik++') + print exp_translator('jk = kk.ik++') #First line translated with PyJs: PyJsStrictEq(PyJsAdd((Js(100)*Js(50)),Js(30)), Js("5030")), yay! print exp_translator('delete a.f') - diff --git a/js2py/legecy_translators/nparser.py b/js2py/legecy_translators/nparser.py index 8cd4d2bd..a9c97c9a 100644 --- a/js2py/legecy_translators/nparser.py +++ b/js2py/legecy_translators/nparser.py @@ -33,6 +33,8 @@ # -*- coding: latin-1 -*- from __future__ import print_function import re + + def typeof(t): if t is None: return 'undefined' elif isinstance(t, bool): return 'boolean' @@ -41,343 +43,398 @@ def typeof(t): elif hasattr(t, '__call__'): return 'function' else: return 'object' + def list_indexOf(l, v): try: return l.index(v) except: return -1 + parseFloat = float parseInt = int + class jsdict(object): def __init__(self, d): self.__dict__.update(d) + def __getitem__(self, name): if name in self.__dict__: - return self.__dict__[name] + return self.__dict__[name] else: - return None + return None + def __setitem__(self, name, value): self.__dict__[name] = value return value + def __getattr__(self, name): try: return getattr(self, name) except: return None + def __setattr__(self, name, value): self[name] = value return value + def __contains__(self, name): return name in self.__dict__ + def __repr__(self): return str(self.__dict__) + class RegExp(object): def __init__(self, pattern, flags=''): self.flags = flags pyflags = 0 | re.M if 'm' in flags else 0 | re.I if 'i' in flags else 0 self.source = pattern self.pattern = re.compile(pattern, pyflags) + def test(self, s): return self.pattern.search(s) is not None -console = jsdict({"log":print}) + +console = jsdict({"log": print}) + def __temp__42(object=None, body=None): return jsdict({ -"type": Syntax.WithStatement, -"object": object, -"body": body, -}) + "type": Syntax.WithStatement, + "object": object, + "body": body, + }) + def __temp__41(test=None, body=None): return jsdict({ -"type": Syntax.WhileStatement, -"test": test, -"body": body, -}) + "type": Syntax.WhileStatement, + "test": test, + "body": body, + }) + def __temp__40(id=None, init=None): return jsdict({ -"type": Syntax.VariableDeclarator, -"id": id, -"init": init, -}) + "type": Syntax.VariableDeclarator, + "id": id, + "init": init, + }) + def __temp__39(declarations=None, kind=None): return jsdict({ -"type": Syntax.VariableDeclaration, -"declarations": declarations, -"kind": kind, -}) + "type": Syntax.VariableDeclaration, + "declarations": declarations, + "kind": kind, + }) + def __temp__38(operator=None, argument=None): if (operator == "++") or (operator == "--"): return jsdict({ -"type": Syntax.UpdateExpression, -"operator": operator, -"argument": argument, -"prefix": True, -}) + "type": Syntax.UpdateExpression, + "operator": operator, + "argument": argument, + "prefix": True, + }) return jsdict({ -"type": Syntax.UnaryExpression, -"operator": operator, -"argument": argument, -"prefix": True, -}) + "type": Syntax.UnaryExpression, + "operator": operator, + "argument": argument, + "prefix": True, + }) -def __temp__37(block=None, guardedHandlers=None, handlers=None, finalizer=None): + +def __temp__37(block=None, guardedHandlers=None, handlers=None, + finalizer=None): return jsdict({ -"type": Syntax.TryStatement, -"block": block, -"guardedHandlers": guardedHandlers, -"handlers": handlers, -"finalizer": finalizer, -}) + "type": Syntax.TryStatement, + "block": block, + "guardedHandlers": guardedHandlers, + "handlers": handlers, + "finalizer": finalizer, + }) + def __temp__36(argument=None): return jsdict({ -"type": Syntax.ThrowStatement, -"argument": argument, -}) + "type": Syntax.ThrowStatement, + "argument": argument, + }) + def __temp__35(): return jsdict({ -"type": Syntax.ThisExpression, -}) + "type": Syntax.ThisExpression, + }) + def __temp__34(discriminant=None, cases=None): return jsdict({ -"type": Syntax.SwitchStatement, -"discriminant": discriminant, -"cases": cases, -}) + "type": Syntax.SwitchStatement, + "discriminant": discriminant, + "cases": cases, + }) + def __temp__33(test=None, consequent=None): return jsdict({ -"type": Syntax.SwitchCase, -"test": test, -"consequent": consequent, -}) + "type": Syntax.SwitchCase, + "test": test, + "consequent": consequent, + }) + def __temp__32(expressions=None): return jsdict({ -"type": Syntax.SequenceExpression, -"expressions": expressions, -}) + "type": Syntax.SequenceExpression, + "expressions": expressions, + }) + def __temp__31(argument=None): return jsdict({ -"type": Syntax.ReturnStatement, -"argument": argument, -}) + "type": Syntax.ReturnStatement, + "argument": argument, + }) + def __temp__30(kind=None, key=None, value=None): return jsdict({ -"type": Syntax.Property, -"key": key, -"value": value, -"kind": kind, -}) + "type": Syntax.Property, + "key": key, + "value": value, + "kind": kind, + }) + def __temp__29(body=None): return jsdict({ -"type": Syntax.Program, -"body": body, -}) + "type": Syntax.Program, + "body": body, + }) + def __temp__28(operator=None, argument=None): return jsdict({ -"type": Syntax.UpdateExpression, -"operator": operator, -"argument": argument, -"prefix": False, -}) + "type": Syntax.UpdateExpression, + "operator": operator, + "argument": argument, + "prefix": False, + }) + def __temp__27(properties=None): return jsdict({ -"type": Syntax.ObjectExpression, -"properties": properties, -}) + "type": Syntax.ObjectExpression, + "properties": properties, + }) + def __temp__26(callee=None, args=None): return jsdict({ -"type": Syntax.NewExpression, -"callee": callee, -"arguments": args, -}) + "type": Syntax.NewExpression, + "callee": callee, + "arguments": args, + }) + def __temp__25(accessor=None, object=None, property=None): return jsdict({ -"type": Syntax.MemberExpression, -"computed": accessor == "[", -"object": object, -"property": property, -}) + "type": Syntax.MemberExpression, + "computed": accessor == "[", + "object": object, + "property": property, + }) + def __temp__24(token=None): return jsdict({ -"type": Syntax.Literal, -"value": token.value, -"raw": source[token.range[0]:token.range[1]], -}) + "type": Syntax.Literal, + "value": token.value, + "raw": source[token.range[0]:token.range[1]], + }) + def __temp__23(label=None, body=None): return jsdict({ -"type": Syntax.LabeledStatement, -"label": label, -"body": body, -}) + "type": Syntax.LabeledStatement, + "label": label, + "body": body, + }) + def __temp__22(test=None, consequent=None, alternate=None): return jsdict({ -"type": Syntax.IfStatement, -"test": test, -"consequent": consequent, -"alternate": alternate, -}) + "type": Syntax.IfStatement, + "test": test, + "consequent": consequent, + "alternate": alternate, + }) + def __temp__21(name=None): return jsdict({ -"type": Syntax.Identifier, -"name": name, -}) + "type": Syntax.Identifier, + "name": name, + }) + def __temp__20(id=None, params=None, defaults=None, body=None): return jsdict({ -"type": Syntax.FunctionExpression, -"id": id, -"params": params, -"defaults": defaults, -"body": body, -"rest": None, -"generator": False, -"expression": False, -}) + "type": Syntax.FunctionExpression, + "id": id, + "params": params, + "defaults": defaults, + "body": body, + "rest": None, + "generator": False, + "expression": False, + }) + def __temp__19(id=None, params=None, defaults=None, body=None): return jsdict({ -"type": Syntax.FunctionDeclaration, -"id": id, -"params": params, -"defaults": defaults, -"body": body, -"rest": None, -"generator": False, -"expression": False, -}) + "type": Syntax.FunctionDeclaration, + "id": id, + "params": params, + "defaults": defaults, + "body": body, + "rest": None, + "generator": False, + "expression": False, + }) + def __temp__18(left=None, right=None, body=None): return jsdict({ -"type": Syntax.ForInStatement, -"left": left, -"right": right, -"body": body, -"each": False, -}) + "type": Syntax.ForInStatement, + "left": left, + "right": right, + "body": body, + "each": False, + }) + def __temp__17(init=None, test=None, update=None, body=None): return jsdict({ -"type": Syntax.ForStatement, -"init": init, -"test": test, -"update": update, -"body": body, -}) + "type": Syntax.ForStatement, + "init": init, + "test": test, + "update": update, + "body": body, + }) + def __temp__16(expression=None): return jsdict({ -"type": Syntax.ExpressionStatement, -"expression": expression, -}) + "type": Syntax.ExpressionStatement, + "expression": expression, + }) + def __temp__15(): return jsdict({ -"type": Syntax.EmptyStatement, -}) + "type": Syntax.EmptyStatement, + }) + def __temp__14(body=None, test=None): return jsdict({ -"type": Syntax.DoWhileStatement, -"body": body, -"test": test, -}) + "type": Syntax.DoWhileStatement, + "body": body, + "test": test, + }) + def __temp__13(): return jsdict({ -"type": Syntax.DebuggerStatement, -}) + "type": Syntax.DebuggerStatement, + }) + def __temp__12(label=None): return jsdict({ -"type": Syntax.ContinueStatement, -"label": label, -}) + "type": Syntax.ContinueStatement, + "label": label, + }) + def __temp__11(test=None, consequent=None, alternate=None): return jsdict({ -"type": Syntax.ConditionalExpression, -"test": test, -"consequent": consequent, -"alternate": alternate, -}) + "type": Syntax.ConditionalExpression, + "test": test, + "consequent": consequent, + "alternate": alternate, + }) + def __temp__10(param=None, body=None): return jsdict({ -"type": Syntax.CatchClause, -"param": param, -"body": body, -}) + "type": Syntax.CatchClause, + "param": param, + "body": body, + }) + def __temp__9(callee=None, args=None): return jsdict({ -"type": Syntax.CallExpression, -"callee": callee, -"arguments": args, -}) + "type": Syntax.CallExpression, + "callee": callee, + "arguments": args, + }) + def __temp__8(label=None): return jsdict({ -"type": Syntax.BreakStatement, -"label": label, -}) + "type": Syntax.BreakStatement, + "label": label, + }) + def __temp__7(body=None): return jsdict({ -"type": Syntax.BlockStatement, -"body": body, -}) + "type": Syntax.BlockStatement, + "body": body, + }) + def __temp__6(operator=None, left=None, right=None): - type = (Syntax.LogicalExpression if (operator == "||") or (operator == "&&") else Syntax.BinaryExpression) + type = (Syntax.LogicalExpression if (operator == "||") or + (operator == "&&") else Syntax.BinaryExpression) return jsdict({ -"type": type, -"operator": operator, -"left": left, -"right": right, -}) + "type": type, + "operator": operator, + "left": left, + "right": right, + }) + def __temp__5(operator=None, left=None, right=None): return jsdict({ -"type": Syntax.AssignmentExpression, -"operator": operator, -"left": left, -"right": right, -}) + "type": Syntax.AssignmentExpression, + "operator": operator, + "left": left, + "right": right, + }) + def __temp__4(elements=None): return jsdict({ -"type": Syntax.ArrayExpression, -"elements": elements, -}) + "type": Syntax.ArrayExpression, + "elements": elements, + }) + def __temp__3(node=None): if extra.source: node.loc.source = extra.source return node + def __temp__2(node=None): if node.range or node.loc: if extra.loc: @@ -389,23 +446,27 @@ def __temp__2(node=None): SyntaxTreeDelegate.markEnd(node) return node + def __temp__1(node=None): if extra.range: node.range = [state.markerStack.pop(), index] if extra.loc: node.loc = jsdict({ -"start": jsdict({ -"line": state.markerStack.pop(), -"column": state.markerStack.pop(), -}), -"end": jsdict({ -"line": lineNumber, -"column": index - lineStart, -}), -}) + "start": + jsdict({ + "line": state.markerStack.pop(), + "column": state.markerStack.pop(), + }), + "end": + jsdict({ + "line": lineNumber, + "column": index - lineStart, + }), + }) SyntaxTreeDelegate.postProcess(node) return node + def __temp__0(): if extra.loc: state.markerStack.append(index - lineStart) @@ -413,6 +474,7 @@ def __temp__0(): if extra.range: state.markerStack.append(index) + Token = None TokenName = None FnExprTokens = None @@ -432,18 +494,17 @@ def __temp__0(): state = None extra = None Token = jsdict({ -"BooleanLiteral": 1, -"EOF": 2, -"Identifier": 3, -"Keyword": 4, -"NullLiteral": 5, -"NumericLiteral": 6, -"Punctuator": 7, -"StringLiteral": 8, -"RegularExpression": 9, -}) -TokenName = jsdict({ -}) + "BooleanLiteral": 1, + "EOF": 2, + "Identifier": 3, + "Keyword": 4, + "NullLiteral": 5, + "NumericLiteral": 6, + "Punctuator": 7, + "StringLiteral": 8, + "RegularExpression": 9, +}) +TokenName = jsdict({}) TokenName[Token.BooleanLiteral] = "Boolean" TokenName[Token.EOF] = "" TokenName[Token.Identifier] = "Identifier" @@ -453,137 +514,216 @@ def __temp__0(): TokenName[Token.Punctuator] = "Punctuator" TokenName[Token.StringLiteral] = "String" TokenName[Token.RegularExpression] = "RegularExpression" -FnExprTokens = ["(", "{", "[", "in", "typeof", "instanceof", "new", "return", "case", "delete", "throw", "void", "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "|=", "^=", ",", "+", "-", "*", "/", "%", "++", "--", "<<", ">>", ">>>", "&", "|", "^", "!", "~", "&&", "||", "?", ":", "===", "==", ">=", "<=", "<", ">", "!=", "!=="] +FnExprTokens = [ + "(", "{", "[", "in", "typeof", "instanceof", "new", "return", "case", + "delete", "throw", "void", "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", + ">>>=", "&=", "|=", "^=", ",", "+", "-", "*", "/", "%", "++", "--", "<<", + ">>", ">>>", "&", "|", "^", "!", "~", "&&", "||", "?", ":", "===", "==", + ">=", "<=", "<", ">", "!=", "!==" +] Syntax = jsdict({ -"AssignmentExpression": "AssignmentExpression", -"ArrayExpression": "ArrayExpression", -"BlockStatement": "BlockStatement", -"BinaryExpression": "BinaryExpression", -"BreakStatement": "BreakStatement", -"CallExpression": "CallExpression", -"CatchClause": "CatchClause", -"ConditionalExpression": "ConditionalExpression", -"ContinueStatement": "ContinueStatement", -"DoWhileStatement": "DoWhileStatement", -"DebuggerStatement": "DebuggerStatement", -"EmptyStatement": "EmptyStatement", -"ExpressionStatement": "ExpressionStatement", -"ForStatement": "ForStatement", -"ForInStatement": "ForInStatement", -"FunctionDeclaration": "FunctionDeclaration", -"FunctionExpression": "FunctionExpression", -"Identifier": "Identifier", -"IfStatement": "IfStatement", -"Literal": "Literal", -"LabeledStatement": "LabeledStatement", -"LogicalExpression": "LogicalExpression", -"MemberExpression": "MemberExpression", -"NewExpression": "NewExpression", -"ObjectExpression": "ObjectExpression", -"Program": "Program", -"Property": "Property", -"ReturnStatement": "ReturnStatement", -"SequenceExpression": "SequenceExpression", -"SwitchStatement": "SwitchStatement", -"SwitchCase": "SwitchCase", -"ThisExpression": "ThisExpression", -"ThrowStatement": "ThrowStatement", -"TryStatement": "TryStatement", -"UnaryExpression": "UnaryExpression", -"UpdateExpression": "UpdateExpression", -"VariableDeclaration": "VariableDeclaration", -"VariableDeclarator": "VariableDeclarator", -"WhileStatement": "WhileStatement", -"WithStatement": "WithStatement", + "AssignmentExpression": "AssignmentExpression", + "ArrayExpression": "ArrayExpression", + "BlockStatement": "BlockStatement", + "BinaryExpression": "BinaryExpression", + "BreakStatement": "BreakStatement", + "CallExpression": "CallExpression", + "CatchClause": "CatchClause", + "ConditionalExpression": "ConditionalExpression", + "ContinueStatement": "ContinueStatement", + "DoWhileStatement": "DoWhileStatement", + "DebuggerStatement": "DebuggerStatement", + "EmptyStatement": "EmptyStatement", + "ExpressionStatement": "ExpressionStatement", + "ForStatement": "ForStatement", + "ForInStatement": "ForInStatement", + "FunctionDeclaration": "FunctionDeclaration", + "FunctionExpression": "FunctionExpression", + "Identifier": "Identifier", + "IfStatement": "IfStatement", + "Literal": "Literal", + "LabeledStatement": "LabeledStatement", + "LogicalExpression": "LogicalExpression", + "MemberExpression": "MemberExpression", + "NewExpression": "NewExpression", + "ObjectExpression": "ObjectExpression", + "Program": "Program", + "Property": "Property", + "ReturnStatement": "ReturnStatement", + "SequenceExpression": "SequenceExpression", + "SwitchStatement": "SwitchStatement", + "SwitchCase": "SwitchCase", + "ThisExpression": "ThisExpression", + "ThrowStatement": "ThrowStatement", + "TryStatement": "TryStatement", + "UnaryExpression": "UnaryExpression", + "UpdateExpression": "UpdateExpression", + "VariableDeclaration": "VariableDeclaration", + "VariableDeclarator": "VariableDeclarator", + "WhileStatement": "WhileStatement", + "WithStatement": "WithStatement", }) PropertyKind = jsdict({ -"Data": 1, -"Get": 2, -"Set": 4, + "Data": 1, + "Get": 2, + "Set": 4, }) Messages = jsdict({ -"UnexpectedToken": "Unexpected token %0", -"UnexpectedNumber": "Unexpected number", -"UnexpectedString": "Unexpected string", -"UnexpectedIdentifier": "Unexpected identifier", -"UnexpectedReserved": "Unexpected reserved word", -"UnexpectedEOS": "Unexpected end of input", -"NewlineAfterThrow": "Illegal newline after throw", -"InvalidRegExp": "Invalid regular expression", -"UnterminatedRegExp": "Invalid regular expression: missing /", -"InvalidLHSInAssignment": "Invalid left-hand side in assignment", -"InvalidLHSInForIn": "Invalid left-hand side in for-in", -"MultipleDefaultsInSwitch": "More than one default clause in switch statement", -"NoCatchOrFinally": "Missing catch or finally after try", -"UnknownLabel": "Undefined label '%0'", -"Redeclaration": "%0 '%1' has already been declared", -"IllegalContinue": "Illegal continue statement", -"IllegalBreak": "Illegal break statement", -"IllegalReturn": "Illegal return statement", -"StrictModeWith": "Strict mode code may not include a with statement", -"StrictCatchVariable": "Catch variable may not be eval or arguments in strict mode", -"StrictVarName": "Variable name may not be eval or arguments in strict mode", -"StrictParamName": "Parameter name eval or arguments is not allowed in strict mode", -"StrictParamDupe": "Strict mode function may not have duplicate parameter names", -"StrictFunctionName": "Function name may not be eval or arguments in strict mode", -"StrictOctalLiteral": "Octal literals are not allowed in strict mode.", -"StrictDelete": "Delete of an unqualified identifier in strict mode.", -"StrictDuplicateProperty": "Duplicate data property in object literal not allowed in strict mode", -"AccessorDataProperty": "Object literal may not have data and accessor property with the same name", -"AccessorGetSet": "Object literal may not have multiple get/set accessors with the same name", -"StrictLHSAssignment": "Assignment to eval or arguments is not allowed in strict mode", -"StrictLHSPostfix": "Postfix increment/decrement may not have eval or arguments operand in strict mode", -"StrictLHSPrefix": "Prefix increment/decrement may not have eval or arguments operand in strict mode", -"StrictReservedWord": "Use of future reserved word in strict mode", + "UnexpectedToken": + "Unexpected token %0", + "UnexpectedNumber": + "Unexpected number", + "UnexpectedString": + "Unexpected string", + "UnexpectedIdentifier": + "Unexpected identifier", + "UnexpectedReserved": + "Unexpected reserved word", + "UnexpectedEOS": + "Unexpected end of input", + "NewlineAfterThrow": + "Illegal newline after throw", + "InvalidRegExp": + "Invalid regular expression", + "UnterminatedRegExp": + "Invalid regular expression: missing /", + "InvalidLHSInAssignment": + "Invalid left-hand side in assignment", + "InvalidLHSInForIn": + "Invalid left-hand side in for-in", + "MultipleDefaultsInSwitch": + "More than one default clause in switch statement", + "NoCatchOrFinally": + "Missing catch or finally after try", + "UnknownLabel": + "Undefined label '%0'", + "Redeclaration": + "%0 '%1' has already been declared", + "IllegalContinue": + "Illegal continue statement", + "IllegalBreak": + "Illegal break statement", + "IllegalReturn": + "Illegal return statement", + "StrictModeWith": + "Strict mode code may not include a with statement", + "StrictCatchVariable": + "Catch variable may not be eval or arguments in strict mode", + "StrictVarName": + "Variable name may not be eval or arguments in strict mode", + "StrictParamName": + "Parameter name eval or arguments is not allowed in strict mode", + "StrictParamDupe": + "Strict mode function may not have duplicate parameter names", + "StrictFunctionName": + "Function name may not be eval or arguments in strict mode", + "StrictOctalLiteral": + "Octal literals are not allowed in strict mode.", + "StrictDelete": + "Delete of an unqualified identifier in strict mode.", + "StrictDuplicateProperty": + "Duplicate data property in object literal not allowed in strict mode", + "AccessorDataProperty": + "Object literal may not have data and accessor property with the same name", + "AccessorGetSet": + "Object literal may not have multiple get/set accessors with the same name", + "StrictLHSAssignment": + "Assignment to eval or arguments is not allowed in strict mode", + "StrictLHSPostfix": + "Postfix increment/decrement may not have eval or arguments operand in strict mode", + "StrictLHSPrefix": + "Prefix increment/decrement may not have eval or arguments operand in strict mode", + "StrictReservedWord": + "Use of future reserved word in strict mode", }) Regex = jsdict({ -"NonAsciiIdentifierStart": RegExp(u"[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]"), -"NonAsciiIdentifierPart": RegExp(u"[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]"), + "NonAsciiIdentifierStart": + RegExp( + u"[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]" + ), + "NonAsciiIdentifierPart": + RegExp( + u"[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]" + ), }) + + def assert__py__(condition=None, message=None): if not condition: raise RuntimeError("ASSERT: " + message) + def isDecimalDigit(ch=None): return (ch >= 48) and (ch <= 57) + def isHexDigit(ch=None): return "0123456789abcdefABCDEF".find(ch) >= 0 + def isOctalDigit(ch=None): return "01234567".find(ch) >= 0 + def isWhiteSpace(ch=None): - return (((((ch == 32) or (ch == 9)) or (ch == 11)) or (ch == 12)) or (ch == 160)) or ((ch >= 5760) and (u"\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff".find(unichr(ch)) > 0)) + return ( + ((((ch == 32) or (ch == 9)) or (ch == 11)) or + (ch == 12)) or (ch == 160) + ) or ((ch >= 5760) and ( + u"\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff" + .find(unichr(ch)) > 0)) + def isLineTerminator(ch=None): return (((ch == 10) or (ch == 13)) or (ch == 8232)) or (ch == 8233) + def isIdentifierStart(ch=None): - return (((((ch == 36) or (ch == 95)) or ((ch >= 65) and (ch <= 90))) or ((ch >= 97) and (ch <= 122))) or (ch == 92)) or ((ch >= 128) and Regex.NonAsciiIdentifierStart.test(unichr(ch))) + return (((((ch == 36) or (ch == 95)) or ((ch >= 65) and (ch <= 90))) or + ((ch >= 97) and (ch <= 122))) or + (ch == 92)) or ((ch >= 128) + and Regex.NonAsciiIdentifierStart.test(unichr(ch))) + def isIdentifierPart(ch=None): - return ((((((ch == 36) or (ch == 95)) or ((ch >= 65) and (ch <= 90))) or ((ch >= 97) and (ch <= 122))) or ((ch >= 48) and (ch <= 57))) or (ch == 92)) or ((ch >= 128) and Regex.NonAsciiIdentifierPart.test(unichr(ch))) + return ((((((ch == 36) or (ch == 95)) or ((ch >= 65) and (ch <= 90))) or + ((ch >= 97) and (ch <= 122))) or ((ch >= 48) and (ch <= 57))) or + (ch == 92)) or ((ch >= 128) + and Regex.NonAsciiIdentifierPart.test(unichr(ch))) + def isFutureReservedWord(id=None): while 1: - if (id == "super") or ((id == "import") or ((id == "extends") or ((id == "export") or ((id == "enum") or (id == "class"))))): + if (id == "super") or ((id == "import") or ((id == "extends") or + ((id == "export") or + ((id == "enum") or + (id == "class"))))): return True else: return False break + def isStrictModeReservedWord(id=None): while 1: - if (id == "let") or ((id == "yield") or ((id == "static") or ((id == "public") or ((id == "protected") or ((id == "private") or ((id == "package") or ((id == "interface") or (id == "implements")))))))): + if (id == "let") or ((id == "yield") or + ((id == "static") or + ((id == "public") or + ((id == "protected") or + ((id == "private") or + ((id == "package") or + ((id == "interface") or + (id == "implements")))))))): return True else: return False break + def isRestrictedWord(id=None): return (id == "eval") or (id == "arguments") + def isKeyword(id=None): if strict and isStrictModeReservedWord(id): return True @@ -591,39 +731,52 @@ def isKeyword(id=None): if len(id) == 2: return ((id == "if") or (id == "in")) or (id == "do") elif len(id) == 3: - return ((((id == "var") or (id == "for")) or (id == "new")) or (id == "try")) or (id == "let") + return ((((id == "var") or (id == "for")) or (id == "new")) or + (id == "try")) or (id == "let") elif len(id) == 4: - return (((((id == "this") or (id == "else")) or (id == "case")) or (id == "void")) or (id == "with")) or (id == "enum") + return (((((id == "this") or (id == "else")) or (id == "case")) or + (id == "void")) or (id == "with")) or (id == "enum") elif len(id) == 5: - return (((((((id == "while") or (id == "break")) or (id == "catch")) or (id == "throw")) or (id == "const")) or (id == "yield")) or (id == "class")) or (id == "super") + return (((((((id == "while") or (id == "break")) or + (id == "catch")) or (id == "throw")) or + (id == "const")) or (id == "yield")) or + (id == "class")) or (id == "super") elif len(id) == 6: - return (((((id == "return") or (id == "typeof")) or (id == "delete")) or (id == "switch")) or (id == "export")) or (id == "import") + return (((((id == "return") or (id == "typeof")) or + (id == "delete")) or (id == "switch")) or + (id == "export")) or (id == "import") elif len(id) == 7: - return ((id == "default") or (id == "finally")) or (id == "extends") + return ((id == "default") or + (id == "finally")) or (id == "extends") elif len(id) == 8: - return ((id == "function") or (id == "continue")) or (id == "debugger") + return ((id == "function") or + (id == "continue")) or (id == "debugger") elif len(id) == 10: return id == "instanceof" else: return False break + def addComment(type=None, value=None, start=None, end=None, loc=None): comment = None - assert__py__(('undefined' if not 'start' in locals() else typeof(start)) == "number", "Comment must have valid position") + assert__py__(('undefined' + if not 'start' in locals() else typeof(start)) == "number", + "Comment must have valid position") if state.lastCommentStart >= start: - return + return state.lastCommentStart = start comment = jsdict({ -"type": type, -"value": value, -}) + "type": type, + "value": value, + }) if extra.range: comment.range = [start, end] if extra.loc: comment.loc = loc extra.comments.append(comment) + def skipSingleLineComment(): global index, lineNumber, lineStart start = None @@ -632,11 +785,12 @@ def skipSingleLineComment(): comment = None start = index - 2 loc = jsdict({ -"start": jsdict({ -"line": lineNumber, -"column": (index - lineStart) - 2, -}), -}) + "start": + jsdict({ + "line": lineNumber, + "column": (index - lineStart) - 2, + }), + }) while index < length: ch = (ord(source[index]) if index < len(source) else None) index += 1 @@ -645,25 +799,27 @@ def skipSingleLineComment(): if extra.comments: comment = source[(start + 2):(index - 1)] loc.end = jsdict({ -"line": lineNumber, -"column": (index - lineStart) - 1, -}) + "line": lineNumber, + "column": (index - lineStart) - 1, + }) addComment("Line", comment, start, index - 1, loc) - if (ch == 13) and ((ord(source[index]) if index < len(source) else None) == 10): + if (ch == 13) and ( + (ord(source[index]) if index < len(source) else None) == 10): index += 1 index lineNumber += 1 lineNumber lineStart = index - return + return if extra.comments: comment = source[(start + 2):index] loc.end = jsdict({ -"line": lineNumber, -"column": index - lineStart, -}) + "line": lineNumber, + "column": index - lineStart, + }) addComment("Line", comment, start, index, loc) + def skipMultiLineComment(): global index, lineNumber, lineStart start = None @@ -673,15 +829,17 @@ def skipMultiLineComment(): if extra.comments: start = index - 2 loc = jsdict({ -"start": jsdict({ -"line": lineNumber, -"column": (index - lineStart) - 2, -}), -}) + "start": + jsdict({ + "line": lineNumber, + "column": (index - lineStart) - 2, + }), + }) while index < length: ch = (ord(source[index]) if index < len(source) else None) if isLineTerminator(ch): - if (ch == 13) and ((ord(source[index + 1]) if (index + 1) < len(source) else None) == 10): + if (ch == 13) and ((ord(source[index + 1]) if + (index + 1) < len(source) else None) == 10): index += 1 index lineNumber += 1 @@ -690,10 +848,10 @@ def skipMultiLineComment(): index lineStart = index if index >= length: - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") elif ch == 42: - if (ord(source[index + 1]) if (index + 1) < len(source) else None) == 47: + if (ord(source[index + 1]) if + (index + 1) < len(source) else None) == 47: index += 1 index index += 1 @@ -701,18 +859,18 @@ def skipMultiLineComment(): if extra.comments: comment = source[(start + 2):(index - 2)] loc.end = jsdict({ -"line": lineNumber, -"column": index - lineStart, -}) + "line": lineNumber, + "column": index - lineStart, + }) addComment("Block", comment, start, index, loc) - return + return index += 1 index else: index += 1 index - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") + def skipComment(): global index, lineNumber, lineStart @@ -725,14 +883,16 @@ def skipComment(): elif isLineTerminator(ch): index += 1 index - if (ch == 13) and ((ord(source[index]) if index < len(source) else None) == 10): + if (ch == 13) and ( + (ord(source[index]) if index < len(source) else None) == 10): index += 1 index lineNumber += 1 lineNumber lineStart = index elif ch == 47: - ch = (ord(source[index + 1]) if (index + 1) < len(source) else None) + ch = (ord(source[index + 1]) if + (index + 1) < len(source) else None) if ch == 47: index += 1 index @@ -750,6 +910,7 @@ def skipComment(): else: break + def scanHexEscape(prefix=None): global len__py__, index i = None @@ -770,6 +931,7 @@ def scanHexEscape(prefix=None): i += 1 return unichr(code) + def getEscapedIdentifier(): global index ch = None @@ -780,14 +942,13 @@ def getEscapedIdentifier(): id = unichr(ch) if ch == 92: if (ord(source[index]) if index < len(source) else None) != 117: - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") index += 1 index ch = scanHexEscape("u") - if ((not ch) or (ch == "\\")) or (not isIdentifierStart((ord(ch[0]) if 0 < len(ch) else None))): - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + if ((not ch) or (ch == "\\")) or (not isIdentifierStart( + (ord(ch[0]) if 0 < len(ch) else None))): + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") id = ch while index < length: ch = (ord(source[index]) if index < len(source) else None) @@ -799,17 +960,17 @@ def getEscapedIdentifier(): if ch == 92: id = id[0:(0 + (len(id) - 1))] if (ord(source[index]) if index < len(source) else None) != 117: - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") index += 1 index ch = scanHexEscape("u") - if ((not ch) or (ch == "\\")) or (not isIdentifierPart((ord(ch[0]) if 0 < len(ch) else None))): - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + if ((not ch) or (ch == "\\")) or (not isIdentifierPart( + (ord(ch[0]) if 0 < len(ch) else None))): + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") id += ch return id + def getIdentifier(): global index start = None @@ -828,12 +989,15 @@ def getIdentifier(): break return source[start:index] + def scanIdentifier(): start = None id = None type = None start = index - id = (getEscapedIdentifier() if (ord(source[index]) if index < len(source) else None) == 92 else getIdentifier()) + id = (getEscapedIdentifier() if + (ord(source[index]) if index < len(source) else None) == 92 else + getIdentifier()) if len(id) == 1: type = Token.Identifier elif isKeyword(id): @@ -845,12 +1009,13 @@ def scanIdentifier(): else: type = Token.Identifier return jsdict({ -"type": type, -"value": id, -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": type, + "value": id, + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) + def scanPunctuator(): global index @@ -862,7 +1027,16 @@ def scanPunctuator(): ch3 = None ch4 = None while 1: - if (code == 126) or ((code == 63) or ((code == 58) or ((code == 93) or ((code == 91) or ((code == 125) or ((code == 123) or ((code == 44) or ((code == 59) or ((code == 41) or ((code == 40) or (code == 46))))))))))): + if (code == 126) or ((code == 63) or ((code == 58) or + ((code == 93) or + ((code == 91) or + ((code == 125) or + ((code == 123) or + ((code == 44) or + ((code == 59) or + ((code == 41) or + ((code == 40) or + (code == 46))))))))))): index += 1 index if extra.tokenize: @@ -871,37 +1045,47 @@ def scanPunctuator(): elif code == 123: extra.openCurlyToken = len(extra.tokens) return jsdict({ -"type": Token.Punctuator, -"value": unichr(code), -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.Punctuator, + "value": unichr(code), + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) else: - code2 = (ord(source[index + 1]) if (index + 1) < len(source) else None) + code2 = (ord(source[index + 1]) if + (index + 1) < len(source) else None) if code2 == 61: while 1: - if (code == 124) or ((code == 94) or ((code == 62) or ((code == 60) or ((code == 47) or ((code == 45) or ((code == 43) or ((code == 42) or ((code == 38) or (code == 37))))))))): + if (code == 124) or ((code == 94) or + ((code == 62) or + ((code == 60) or + ((code == 47) or + ((code == 45) or + ((code == 43) or + ((code == 42) or + ((code == 38) or + (code == 37))))))))): index += 2 return jsdict({ -"type": Token.Punctuator, -"value": unichr(code) + unichr(code2), -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.Punctuator, + "value": unichr(code) + unichr(code2), + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) elif (code == 61) or (code == 33): index += 2 - if (ord(source[index]) if index < len(source) else None) == 61: + if (ord(source[index]) + if index < len(source) else None) == 61: index += 1 index return jsdict({ -"type": Token.Punctuator, -"value": source[start:index], -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.Punctuator, + "value": source[start:index], + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) else: break break @@ -914,60 +1098,60 @@ def scanPunctuator(): if ch4 == "=": index += 4 return jsdict({ -"type": Token.Punctuator, -"value": ">>>=", -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.Punctuator, + "value": ">>>=", + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) if ((ch1 == ">") and (ch2 == ">")) and (ch3 == ">"): index += 3 return jsdict({ -"type": Token.Punctuator, -"value": ">>>", -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.Punctuator, + "value": ">>>", + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) if ((ch1 == "<") and (ch2 == "<")) and (ch3 == "="): index += 3 return jsdict({ -"type": Token.Punctuator, -"value": "<<=", -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.Punctuator, + "value": "<<=", + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) if ((ch1 == ">") and (ch2 == ">")) and (ch3 == "="): index += 3 return jsdict({ -"type": Token.Punctuator, -"value": ">>=", -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.Punctuator, + "value": ">>=", + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) if (ch1 == ch2) and ("+-<>&|".find(ch1) >= 0): index += 2 return jsdict({ -"type": Token.Punctuator, -"value": ch1 + ch2, -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.Punctuator, + "value": ch1 + ch2, + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) if "<>=!+-*%&|^/".find(ch1) >= 0: index += 1 index return jsdict({ -"type": Token.Punctuator, -"value": ch1, -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + "type": Token.Punctuator, + "value": ch1, + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") + def scanHexLiteral(start=None): global index @@ -978,18 +1162,18 @@ def scanHexLiteral(start=None): index += 1 number += source[index - 1] if len(number) == 0: - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") - if isIdentifierStart((ord(source[index]) if index < len(source) else None)): - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") + if isIdentifierStart( + (ord(source[index]) if index < len(source) else None)): + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") return jsdict({ -"type": Token.NumericLiteral, -"value": parseInt("0x" + number, 16), -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.NumericLiteral, + "value": parseInt("0x" + number, 16), + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) + def scanOctalLiteral(start=None): global index @@ -1000,17 +1184,20 @@ def scanOctalLiteral(start=None): break index += 1 number += source[index - 1] - if isIdentifierStart((ord(source[index]) if index < len(source) else None)) or isDecimalDigit((ord(source[index]) if index < len(source) else None)): - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + if isIdentifierStart( + (ord(source[index]) + if index < len(source) else None)) or isDecimalDigit( + (ord(source[index]) if index < len(source) else None)): + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") return jsdict({ -"type": Token.NumericLiteral, -"value": parseInt(number, 8), -"octal": True, -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.NumericLiteral, + "value": parseInt(number, 8), + "octal": True, + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) + def scanNumericLiteral(): global index @@ -1018,7 +1205,9 @@ def scanNumericLiteral(): start = None ch = None ch = source[index] - assert__py__(isDecimalDigit((ord(ch[0]) if 0 < len(ch) else None)) or (ch == "."), "Numeric literal must start with a decimal digit or a decimal point") + assert__py__( + isDecimalDigit((ord(ch[0]) if 0 < len(ch) else None)) or (ch == "."), + "Numeric literal must start with a decimal digit or a decimal point") start = index number = "" if ch != ".": @@ -1033,16 +1222,17 @@ def scanNumericLiteral(): if isOctalDigit(ch): return scanOctalLiteral(start) if ch and isDecimalDigit((ord(ch[0]) if 0 < len(ch) else None)): - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") - while isDecimalDigit((ord(source[index]) if index < len(source) else None)): + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") + while isDecimalDigit( + (ord(source[index]) if index < len(source) else None)): index += 1 number += source[index - 1] ch = source[index] if index < len(source) else None if ch == ".": index += 1 number += source[index - 1] - while isDecimalDigit((ord(source[index]) if index < len(source) else None)): + while isDecimalDigit( + (ord(source[index]) if index < len(source) else None)): index += 1 number += source[index - 1] ch = source[index] @@ -1053,23 +1243,25 @@ def scanNumericLiteral(): if (ch == "+") or (ch == "-"): index += 1 number += source[index - 1] - if isDecimalDigit((ord(source[index]) if index < len(source) else None)): - while isDecimalDigit((ord(source[index]) if index < len(source) else None)): + if isDecimalDigit( + (ord(source[index]) if index < len(source) else None)): + while isDecimalDigit( + (ord(source[index]) if index < len(source) else None)): index += 1 number += source[index - 1] else: - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") - if isIdentifierStart((ord(source[index]) if index < len(source) else None)): - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") + if isIdentifierStart( + (ord(source[index]) if index < len(source) else None)): + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") return jsdict({ -"type": Token.NumericLiteral, -"value": parseFloat(number), -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.NumericLiteral, + "value": parseFloat(number), + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) + def scanStringLiteral(): global index, lineNumber @@ -1082,7 +1274,8 @@ def scanStringLiteral(): restore = None octal = False quote = source[index] - assert__py__((quote == "'") or (quote == "\""), "String literal must starts with a quote") + assert__py__((quote == "'") or (quote == "\""), + "String literal must starts with a quote") start = index index += 1 index @@ -1095,7 +1288,8 @@ def scanStringLiteral(): elif ch == "\\": index += 1 ch = source[index - 1] - if (not ch) or (not isLineTerminator((ord(ch[0]) if 0 < len(ch) else None))): + if (not ch) or (not isLineTerminator( + (ord(ch[0]) if 0 < len(ch) else None))): while 1: if ch == "n": str += u"\x0a" @@ -1129,13 +1323,18 @@ def scanStringLiteral(): code = "01234567".find(ch) if code != 0: octal = True - if (index < length) and isOctalDigit(source[index]): + if (index < length) and isOctalDigit( + source[index]): octal = True index += 1 - code = (code * 8) + "01234567".find(source[index - 1]) - if (("0123".find(ch) >= 0) and (index < length)) and isOctalDigit(source[index]): + code = (code * 8) + "01234567".find( + source[index - 1]) + if (("0123".find(ch) >= 0) and + (index < length)) and isOctalDigit( + source[index]): index += 1 - code = (code * 8) + "01234567".find(source[index - 1]) + code = (code * 8) + "01234567".find( + source[index - 1]) str += unichr(code) else: str += ch @@ -1152,16 +1351,16 @@ def scanStringLiteral(): else: str += ch if quote != "": - throwError(jsdict({ -}), Messages.UnexpectedToken, "ILLEGAL") + throwError(jsdict({}), Messages.UnexpectedToken, "ILLEGAL") return jsdict({ -"type": Token.StringLiteral, -"value": str, -"octal": octal, -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.StringLiteral, + "value": str, + "octal": octal, + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) + def scanRegExp(): global lookahead, index @@ -1178,7 +1377,8 @@ def scanRegExp(): skipComment() start = index ch = source[index] - assert__py__(ch == "/", "Regular expression literal must start with a slash") + assert__py__(ch == "/", + "Regular expression literal must start with a slash") index += 1 str = source[index - 1] while index < length: @@ -1193,8 +1393,7 @@ def scanRegExp(): index += 1 ch = source[index - 1] if isLineTerminator((ord(ch[0]) if 0 < len(ch) else None)): - throwError(jsdict({ -}), Messages.UnterminatedRegExp) + throwError(jsdict({}), Messages.UnterminatedRegExp) str += ch elif ch == "/": terminated = True @@ -1202,11 +1401,9 @@ def scanRegExp(): elif ch == "[": classMarker = True elif isLineTerminator((ord(ch[0]) if 0 < len(ch) else None)): - throwError(jsdict({ -}), Messages.UnterminatedRegExp) + throwError(jsdict({}), Messages.UnterminatedRegExp) if not terminated: - throwError(jsdict({ -}), Messages.UnterminatedRegExp) + throwError(jsdict({}), Messages.UnterminatedRegExp) pattern = str[1:(1 + (len(str) - 2))] flags = "" while index < length: @@ -1242,25 +1439,29 @@ def scanRegExp(): try: value = RegExp(pattern, flags) except Exception as e: - throwError(jsdict({ -}), Messages.InvalidRegExp) + throwError(jsdict({}), Messages.InvalidRegExp) peek() if extra.tokenize: return jsdict({ -"type": Token.RegularExpression, -"value": value, -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [start, index], -}) + "type": Token.RegularExpression, + "value": value, + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [start, index], + }) return jsdict({ -"literal": str, -"value": value, -"range": [start, index], -}) + "literal": str, + "value": value, + "range": [start, index], + }) + def isIdentifierName(token=None): - return (((token.type == Token.Identifier) or (token.type == Token.Keyword)) or (token.type == Token.BooleanLiteral)) or (token.type == Token.NullLiteral) + return (((token.type == Token.Identifier) or + (token.type == Token.Keyword)) or + (token.type == Token.BooleanLiteral)) or ( + token.type == Token.NullLiteral) + def advanceSlash(): prevToken = None @@ -1271,15 +1472,20 @@ def advanceSlash(): if prevToken.type == "Punctuator": if prevToken.value == ")": checkToken = extra.tokens[extra.openParenToken - 1] - if (checkToken and (checkToken.type == "Keyword")) and ((((checkToken.value == "if") or (checkToken.value == "while")) or (checkToken.value == "for")) or (checkToken.value == "with")): + if (checkToken and (checkToken.type == "Keyword")) and ( + (((checkToken.value == "if") or + (checkToken.value == "while")) or + (checkToken.value == "for")) or (checkToken.value == "with")): return scanRegExp() return scanPunctuator() if prevToken.value == "}": - if extra.tokens[extra.openCurlyToken - 3] and (extra.tokens[extra.openCurlyToken - 3].type == "Keyword"): + if extra.tokens[extra.openCurlyToken - 3] and ( + extra.tokens[extra.openCurlyToken - 3].type == "Keyword"): checkToken = extra.tokens[extra.openCurlyToken - 4] if not checkToken: return scanPunctuator() - elif extra.tokens[extra.openCurlyToken - 4] and (extra.tokens[extra.openCurlyToken - 4].type == "Keyword"): + elif extra.tokens[extra.openCurlyToken - 4] and ( + extra.tokens[extra.openCurlyToken - 4].type == "Keyword"): checkToken = extra.tokens[extra.openCurlyToken - 5] if not checkToken: return scanRegExp() @@ -1293,16 +1499,17 @@ def advanceSlash(): return scanRegExp() return scanPunctuator() + def advance(): ch = None skipComment() if index >= length: return jsdict({ -"type": Token.EOF, -"lineNumber": lineNumber, -"lineStart": lineStart, -"range": [index, index], -}) + "type": Token.EOF, + "lineNumber": lineNumber, + "lineStart": lineStart, + "range": [index, index], + }) ch = (ord(source[index]) if index < len(source) else None) if ((ch == 40) or (ch == 41)) or (ch == 58): return scanPunctuator() @@ -1311,7 +1518,8 @@ def advance(): if isIdentifierStart(ch): return scanIdentifier() if ch == 46: - if isDecimalDigit((ord(source[index + 1]) if (index + 1) < len(source) else None)): + if isDecimalDigit((ord(source[index + 1]) if + (index + 1) < len(source) else None)): return scanNumericLiteral() return scanPunctuator() if isDecimalDigit(ch): @@ -1320,6 +1528,7 @@ def advance(): return advanceSlash() return scanPunctuator() + def lex(): global index, lineNumber, lineStart, lookahead token = None @@ -1333,6 +1542,7 @@ def lex(): lineStart = token.lineStart return token + def peek(): global lookahead, index, lineNumber, lineStart pos = None @@ -1346,52 +1556,55 @@ def peek(): lineNumber = line lineStart = start + SyntaxTreeDelegate = jsdict({ -"name": "SyntaxTree", -"markStart": __temp__0, -"markEnd": __temp__1, -"markEndIf": __temp__2, -"postProcess": __temp__3, -"createArrayExpression": __temp__4, -"createAssignmentExpression": __temp__5, -"createBinaryExpression": __temp__6, -"createBlockStatement": __temp__7, -"createBreakStatement": __temp__8, -"createCallExpression": __temp__9, -"createCatchClause": __temp__10, -"createConditionalExpression": __temp__11, -"createContinueStatement": __temp__12, -"createDebuggerStatement": __temp__13, -"createDoWhileStatement": __temp__14, -"createEmptyStatement": __temp__15, -"createExpressionStatement": __temp__16, -"createForStatement": __temp__17, -"createForInStatement": __temp__18, -"createFunctionDeclaration": __temp__19, -"createFunctionExpression": __temp__20, -"createIdentifier": __temp__21, -"createIfStatement": __temp__22, -"createLabeledStatement": __temp__23, -"createLiteral": __temp__24, -"createMemberExpression": __temp__25, -"createNewExpression": __temp__26, -"createObjectExpression": __temp__27, -"createPostfixExpression": __temp__28, -"createProgram": __temp__29, -"createProperty": __temp__30, -"createReturnStatement": __temp__31, -"createSequenceExpression": __temp__32, -"createSwitchCase": __temp__33, -"createSwitchStatement": __temp__34, -"createThisExpression": __temp__35, -"createThrowStatement": __temp__36, -"createTryStatement": __temp__37, -"createUnaryExpression": __temp__38, -"createVariableDeclaration": __temp__39, -"createVariableDeclarator": __temp__40, -"createWhileStatement": __temp__41, -"createWithStatement": __temp__42, + "name": "SyntaxTree", + "markStart": __temp__0, + "markEnd": __temp__1, + "markEndIf": __temp__2, + "postProcess": __temp__3, + "createArrayExpression": __temp__4, + "createAssignmentExpression": __temp__5, + "createBinaryExpression": __temp__6, + "createBlockStatement": __temp__7, + "createBreakStatement": __temp__8, + "createCallExpression": __temp__9, + "createCatchClause": __temp__10, + "createConditionalExpression": __temp__11, + "createContinueStatement": __temp__12, + "createDebuggerStatement": __temp__13, + "createDoWhileStatement": __temp__14, + "createEmptyStatement": __temp__15, + "createExpressionStatement": __temp__16, + "createForStatement": __temp__17, + "createForInStatement": __temp__18, + "createFunctionDeclaration": __temp__19, + "createFunctionExpression": __temp__20, + "createIdentifier": __temp__21, + "createIfStatement": __temp__22, + "createLabeledStatement": __temp__23, + "createLiteral": __temp__24, + "createMemberExpression": __temp__25, + "createNewExpression": __temp__26, + "createObjectExpression": __temp__27, + "createPostfixExpression": __temp__28, + "createProgram": __temp__29, + "createProperty": __temp__30, + "createReturnStatement": __temp__31, + "createSequenceExpression": __temp__32, + "createSwitchCase": __temp__33, + "createSwitchStatement": __temp__34, + "createThisExpression": __temp__35, + "createThrowStatement": __temp__36, + "createTryStatement": __temp__37, + "createUnaryExpression": __temp__38, + "createVariableDeclaration": __temp__39, + "createVariableDeclarator": __temp__40, + "createWhileStatement": __temp__41, + "createWithStatement": __temp__42, }) + + def peekLineTerminator(): global index, lineNumber, lineStart pos = None @@ -1408,15 +1621,17 @@ def peekLineTerminator(): lineStart = start return found + def throwError(token=None, messageFormat=None, a=None): def __temp__43(whole=None, index=None): assert__py__(index < len(args), "Message reference must be in range") return args[index] - + error = None args = Array.prototype.slice.call(arguments, 2) msg = messageFormat.replace(RegExp(r'%(\d)'), __temp__43) - if ('undefined' if not ('lineNumber' in token) else typeof(token.lineNumber)) == "number": + if ('undefined' if not ('lineNumber' in token) else typeof( + token.lineNumber)) == "number": error = RuntimeError((("Line " + token.lineNumber) + ": ") + msg) error.index = token.range[0] error.lineNumber = token.lineNumber @@ -1429,6 +1644,7 @@ def __temp__43(whole=None, index=None): error.description = msg raise error + def throwErrorTolerant(): try: throwError.apply(None, arguments) @@ -1436,7 +1652,8 @@ def throwErrorTolerant(): if extra.errors: extra.errors.append(e) else: - raise + raise + def throwUnexpected(token=None): if token.type == Token.EOF: @@ -1452,50 +1669,62 @@ def throwUnexpected(token=None): throwError(token, Messages.UnexpectedReserved) elif strict and isStrictModeReservedWord(token.value): throwErrorTolerant(token, Messages.StrictReservedWord) - return + return throwError(token, Messages.UnexpectedToken, token.value) throwError(token, Messages.UnexpectedToken, token.value) + def expect(value=None): token = lex() if (token.type != Token.Punctuator) or (token.value != value): throwUnexpected(token) + def expectKeyword(keyword=None): token = lex() if (token.type != Token.Keyword) or (token.value != keyword): throwUnexpected(token) + def match(value=None): return (lookahead.type == Token.Punctuator) and (lookahead.value == value) + def matchKeyword(keyword=None): return (lookahead.type == Token.Keyword) and (lookahead.value == keyword) + def matchAssign(): op = None if lookahead.type != Token.Punctuator: return False op = lookahead.value - return (((((((((((op == "=") or (op == "*=")) or (op == "/=")) or (op == "%=")) or (op == "+=")) or (op == "-=")) or (op == "<<=")) or (op == ">>=")) or (op == ">>>=")) or (op == "&=")) or (op == "^=")) or (op == "|=") + return (((((((((((op == "=") or (op == "*=")) or (op == "/=")) or + (op == "%=")) or (op == "+=")) or (op == "-=")) or + (op == "<<=")) or (op == ">>=")) or (op == ">>>=")) or + (op == "&=")) or (op == "^=")) or (op == "|=") + def consumeSemicolon(): line = None if (ord(source[index]) if index < len(source) else None) == 59: lex() - return + return line = lineNumber skipComment() if lineNumber != line: - return + return if match(";"): lex() - return + return if (lookahead.type != Token.EOF) and (not match("}")): throwUnexpected(lookahead) + def isLeftHandSide(expr=None): - return (expr.type == Syntax.Identifier) or (expr.type == Syntax.MemberExpression) + return (expr.type == Syntax.Identifier) or ( + expr.type == Syntax.MemberExpression) + def parseArrayInitialiser(): elements = [] @@ -1511,6 +1740,7 @@ def parseArrayInitialiser(): expect("]") return delegate.createArrayExpression(elements) + def parsePropertyFunction(param=None, first=None): global strict previousStrict = None @@ -1522,19 +1752,23 @@ def parsePropertyFunction(param=None, first=None): if (first and strict) and isRestrictedWord(param[0].name): throwErrorTolerant(first, Messages.StrictParamName) strict = previousStrict - return delegate.markEnd(delegate.createFunctionExpression(None, param, [], body)) + return delegate.markEnd( + delegate.createFunctionExpression(None, param, [], body)) + def parseObjectPropertyKey(): token = None skipComment() delegate.markStart() token = lex() - if (token.type == Token.StringLiteral) or (token.type == Token.NumericLiteral): + if (token.type == Token.StringLiteral) or ( + token.type == Token.NumericLiteral): if strict and token.octal: throwErrorTolerant(token, Messages.StrictOctalLiteral) return delegate.markEnd(delegate.createLiteral(token)) return delegate.markEnd(delegate.createIdentifier(token.value)) + def parseObjectProperty(): token = None key = None @@ -1558,7 +1792,8 @@ def parseObjectProperty(): token = lookahead if token.type != Token.Identifier: expect(")") - throwErrorTolerant(token, Messages.UnexpectedToken, token.value) + throwErrorTolerant(token, Messages.UnexpectedToken, + token.value) value = parsePropertyFunction([]) else: param = [parseVariableIdentifier()] @@ -1576,14 +1811,14 @@ def parseObjectProperty(): value = parseAssignmentExpression() return delegate.markEnd(delegate.createProperty("init", key, value)) + def parseObjectInitialiser(): properties = [] property = None name = None key = None kind = None - map = jsdict({ -}) + map = jsdict({}) toString = str expect("{") while not match("}"): @@ -1592,23 +1827,23 @@ def parseObjectInitialiser(): name = property.key.name else: name = toString(property.key.value) - kind = (PropertyKind.Data if property.kind == "init" else (PropertyKind.Get if property.kind == "get" else PropertyKind.Set)) + kind = (PropertyKind.Data if property.kind == "init" else ( + PropertyKind.Get if property.kind == "get" else PropertyKind.Set)) key = "$" + name if key in map: if map[key] == PropertyKind.Data: if strict and (kind == PropertyKind.Data): - throwErrorTolerant(jsdict({ -}), Messages.StrictDuplicateProperty) + throwErrorTolerant( + jsdict({}), Messages.StrictDuplicateProperty) elif kind != PropertyKind.Data: - throwErrorTolerant(jsdict({ -}), Messages.AccessorDataProperty) + throwErrorTolerant( + jsdict({}), Messages.AccessorDataProperty) else: if kind == PropertyKind.Data: - throwErrorTolerant(jsdict({ -}), Messages.AccessorDataProperty) + throwErrorTolerant( + jsdict({}), Messages.AccessorDataProperty) elif map[key] & kind: - throwErrorTolerant(jsdict({ -}), Messages.AccessorGetSet) + throwErrorTolerant(jsdict({}), Messages.AccessorGetSet) map[key] |= kind else: map[key] = kind @@ -1618,6 +1853,7 @@ def parseObjectInitialiser(): expect("}") return delegate.createObjectExpression(properties) + def parseGroupExpression(): expr = None expect("(") @@ -1625,6 +1861,7 @@ def parseGroupExpression(): expect(")") return expr + def parsePrimaryExpression(): type = None token = None @@ -1663,6 +1900,7 @@ def parsePrimaryExpression(): return delegate.markEnd(expr) throwUnexpected(lex()) + def parseArguments(): args = [] expect("(") @@ -1675,6 +1913,7 @@ def parseArguments(): expect(")") return args + def parseNonComputedProperty(): token = None delegate.markStart() @@ -1683,10 +1922,12 @@ def parseNonComputedProperty(): throwUnexpected(token) return delegate.markEnd(delegate.createIdentifier(token.value)) + def parseNonComputedMember(): expect(".") return parseNonComputedProperty() + def parseComputedMember(): expr = None expect("[") @@ -1694,6 +1935,7 @@ def parseComputedMember(): expect("]") return expr + def parseNewExpression(): callee = None args = None @@ -1703,13 +1945,15 @@ def parseNewExpression(): args = (parseArguments() if match("(") else []) return delegate.markEnd(delegate.createNewExpression(callee, args)) + def parseLeftHandSideExpressionAllowCall(): marker = None expr = None args = None property = None marker = createLocationMarker() - expr = (parseNewExpression() if matchKeyword("new") else parsePrimaryExpression()) + expr = (parseNewExpression() + if matchKeyword("new") else parsePrimaryExpression()) while (match(".") or match("[")) or match("("): if match("("): args = parseArguments() @@ -1725,12 +1969,14 @@ def parseLeftHandSideExpressionAllowCall(): marker.apply(expr) return expr + def parseLeftHandSideExpression(): marker = None expr = None property = None marker = createLocationMarker() - expr = (parseNewExpression() if matchKeyword("new") else parsePrimaryExpression()) + expr = (parseNewExpression() + if matchKeyword("new") else parsePrimaryExpression()) while match(".") or match("["): if match("["): property = parseComputedMember() @@ -1743,6 +1989,7 @@ def parseLeftHandSideExpression(): marker.apply(expr) return expr + def parsePostfixExpression(): expr = None token = None @@ -1750,47 +1997,50 @@ def parsePostfixExpression(): expr = parseLeftHandSideExpressionAllowCall() if lookahead.type == Token.Punctuator: if (match("++") or match("--")) and (not peekLineTerminator()): - if (strict and (expr.type == Syntax.Identifier)) and isRestrictedWord(expr.name): - throwErrorTolerant(jsdict({ -}), Messages.StrictLHSPostfix) + if (strict and + (expr.type == Syntax.Identifier)) and isRestrictedWord( + expr.name): + throwErrorTolerant(jsdict({}), Messages.StrictLHSPostfix) if not isLeftHandSide(expr): - throwError(jsdict({ -}), Messages.InvalidLHSInAssignment) + throwError(jsdict({}), Messages.InvalidLHSInAssignment) token = lex() expr = delegate.createPostfixExpression(token.value, expr) return delegate.markEndIf(expr) + def parseUnaryExpression(): token = None expr = None delegate.markStart() - if (lookahead.type != Token.Punctuator) and (lookahead.type != Token.Keyword): + if (lookahead.type != Token.Punctuator) and (lookahead.type != + Token.Keyword): expr = parsePostfixExpression() elif match("++") or match("--"): token = lex() expr = parseUnaryExpression() - if (strict and (expr.type == Syntax.Identifier)) and isRestrictedWord(expr.name): - throwErrorTolerant(jsdict({ -}), Messages.StrictLHSPrefix) + if (strict and + (expr.type == Syntax.Identifier)) and isRestrictedWord(expr.name): + throwErrorTolerant(jsdict({}), Messages.StrictLHSPrefix) if not isLeftHandSide(expr): - throwError(jsdict({ -}), Messages.InvalidLHSInAssignment) + throwError(jsdict({}), Messages.InvalidLHSInAssignment) expr = delegate.createUnaryExpression(token.value, expr) elif ((match("+") or match("-")) or match("~")) or match("!"): token = lex() expr = parseUnaryExpression() expr = delegate.createUnaryExpression(token.value, expr) - elif (matchKeyword("delete") or matchKeyword("void")) or matchKeyword("typeof"): + elif (matchKeyword("delete") + or matchKeyword("void")) or matchKeyword("typeof"): token = lex() expr = parseUnaryExpression() expr = delegate.createUnaryExpression(token.value, expr) - if (strict and (expr.operator == "delete")) and (expr.argument.type == Syntax.Identifier): - throwErrorTolerant(jsdict({ -}), Messages.StrictDelete) + if (strict and (expr.operator == "delete")) and ( + expr.argument.type == Syntax.Identifier): + throwErrorTolerant(jsdict({}), Messages.StrictDelete) else: expr = parsePostfixExpression() return delegate.markEndIf(expr) + def binaryPrecedence(token=None, allowIn=None): prec = 0 if (token.type != Token.Punctuator) and (token.type != Token.Keyword): @@ -1811,22 +2061,29 @@ def binaryPrecedence(token=None, allowIn=None): elif token.value == "&": prec = 5 break - elif (token.value == "!==") or ((token.value == "===") or ((token.value == "!=") or (token.value == "=="))): + elif (token.value == "!==") or ((token.value == "===") or + ((token.value == "!=") or + (token.value == "=="))): prec = 6 break - elif (token.value == "instanceof") or ((token.value == ">=") or ((token.value == "<=") or ((token.value == ">") or (token.value == "<")))): + elif (token.value == "instanceof") or ((token.value == ">=") or + ((token.value == "<=") or + ((token.value == ">") or + (token.value == "<")))): prec = 7 break elif token.value == "in": prec = (7 if allowIn else 0) break - elif (token.value == ">>>") or ((token.value == ">>") or (token.value == "<<")): + elif (token.value == ">>>") or ((token.value == ">>") or + (token.value == "<<")): prec = 8 break elif (token.value == "-") or (token.value == "+"): prec = 9 break - elif (token.value == "%") or ((token.value == "/") or (token.value == "*")): + elif (token.value == "%") or ((token.value == "/") or + (token.value == "*")): prec = 11 break else: @@ -1834,6 +2091,7 @@ def binaryPrecedence(token=None, allowIn=None): break return prec + def parseBinaryExpression(): marker = None markers = None @@ -1885,7 +2143,8 @@ def parseBinaryExpression(): expr = stack[i] markers.pop() while i > 1: - expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr) + expr = delegate.createBinaryExpression(stack[i - 1].value, + stack[i - 2], expr) i -= 2 marker = markers.pop() if marker: @@ -1893,6 +2152,7 @@ def parseBinaryExpression(): marker.apply(expr) return expr + def parseConditionalExpression(): expr = None previousAllowIn = None @@ -1908,12 +2168,13 @@ def parseConditionalExpression(): state.allowIn = previousAllowIn expect(":") alternate = parseAssignmentExpression() - expr = delegate.markEnd(delegate.createConditionalExpression(expr, consequent, alternate)) + expr = delegate.markEnd( + delegate.createConditionalExpression(expr, consequent, alternate)) else: - delegate.markEnd(jsdict({ -})) + delegate.markEnd(jsdict({})) return expr + def parseAssignmentExpression(): token = None left = None @@ -1925,15 +2186,16 @@ def parseAssignmentExpression(): node = left if matchAssign(): if not isLeftHandSide(left): - throwError(jsdict({ -}), Messages.InvalidLHSInAssignment) - if (strict and (left.type == Syntax.Identifier)) and isRestrictedWord(left.name): + throwError(jsdict({}), Messages.InvalidLHSInAssignment) + if (strict and + (left.type == Syntax.Identifier)) and isRestrictedWord(left.name): throwErrorTolerant(token, Messages.StrictLHSAssignment) token = lex() right = parseAssignmentExpression() node = delegate.createAssignmentExpression(token.value, left, right) return delegate.markEndIf(node) + def parseExpression(): expr = None delegate.markStart() @@ -1947,6 +2209,7 @@ def parseExpression(): expr.expressions.append(parseAssignmentExpression()) return delegate.markEndIf(expr) + def parseStatementList(): list__py__ = [] statement = None @@ -1954,11 +2217,13 @@ def parseStatementList(): if match("}"): break statement = parseSourceElement() - if ('undefined' if not 'statement' in locals() else typeof(statement)) == "undefined": + if ('undefined' if not 'statement' in locals() else + typeof(statement)) == "undefined": break list__py__.append(statement) return list__py__ + def parseBlock(): block = None skipComment() @@ -1968,6 +2233,7 @@ def parseBlock(): expect("}") return delegate.markEnd(delegate.createBlockStatement(block)) + def parseVariableIdentifier(): token = None skipComment() @@ -1977,6 +2243,7 @@ def parseVariableIdentifier(): throwUnexpected(token) return delegate.markEnd(delegate.createIdentifier(token.value)) + def parseVariableDeclaration(kind=None): init = None id = None @@ -1984,8 +2251,7 @@ def parseVariableDeclaration(kind=None): delegate.markStart() id = parseVariableIdentifier() if strict and isRestrictedWord(id.name): - throwErrorTolerant(jsdict({ -}), Messages.StrictVarName) + throwErrorTolerant(jsdict({}), Messages.StrictVarName) if kind == "const": expect("=") init = parseAssignmentExpression() @@ -1994,6 +2260,7 @@ def parseVariableDeclaration(kind=None): init = parseAssignmentExpression() return delegate.markEnd(delegate.createVariableDeclarator(id, init)) + def parseVariableDeclarationList(kind=None): list__py__ = [] while 1: @@ -2005,6 +2272,7 @@ def parseVariableDeclarationList(kind=None): break return list__py__ + def parseVariableStatement(): declarations = None expectKeyword("var") @@ -2012,6 +2280,7 @@ def parseVariableStatement(): consumeSemicolon() return delegate.createVariableDeclaration(declarations, "var") + def parseConstLetDeclaration(kind=None): declarations = None skipComment() @@ -2019,17 +2288,21 @@ def parseConstLetDeclaration(kind=None): expectKeyword(kind) declarations = parseVariableDeclarationList(kind) consumeSemicolon() - return delegate.markEnd(delegate.createVariableDeclaration(declarations, kind)) + return delegate.markEnd( + delegate.createVariableDeclaration(declarations, kind)) + def parseEmptyStatement(): expect(";") return delegate.createEmptyStatement() + def parseExpressionStatement(): expr = parseExpression() consumeSemicolon() return delegate.createExpressionStatement(expr) + def parseIfStatement(): test = None consequent = None @@ -2046,6 +2319,7 @@ def parseIfStatement(): alternate = None return delegate.createIfStatement(test, consequent, alternate) + def parseDoWhileStatement(): body = None test = None @@ -2063,6 +2337,7 @@ def parseDoWhileStatement(): lex() return delegate.createDoWhileStatement(body, test) + def parseWhileStatement(): test = None body = None @@ -2077,13 +2352,16 @@ def parseWhileStatement(): state.inIteration = oldInIteration return delegate.createWhileStatement(test, body) + def parseForVariableDeclaration(): token = None declarations = None delegate.markStart() token = lex() declarations = parseVariableDeclarationList() - return delegate.markEnd(delegate.createVariableDeclaration(declarations, token.value)) + return delegate.markEnd( + delegate.createVariableDeclaration(declarations, token.value)) + def parseForStatement(): init = None @@ -2116,15 +2394,16 @@ def parseForStatement(): state.allowIn = True if matchKeyword("in"): if not isLeftHandSide(init): - throwError(jsdict({ -}), Messages.InvalidLHSInForIn) + throwError(jsdict({}), Messages.InvalidLHSInForIn) lex() left = init right = parseExpression() init = None - if ('undefined' if not 'left' in locals() else typeof(left)) == "undefined": + if ('undefined' + if not 'left' in locals() else typeof(left)) == "undefined": expect(";") - if ('undefined' if not 'left' in locals() else typeof(left)) == "undefined": + if ('undefined' + if not 'left' in locals() else typeof(left)) == "undefined": if not match(";"): test = parseExpression() expect(";") @@ -2135,7 +2414,10 @@ def parseForStatement(): state.inIteration = True body = parseStatement() state.inIteration = oldInIteration - return (delegate.createForStatement(init, test, update, body) if ('undefined' if not 'left' in locals() else typeof(left)) == "undefined" else delegate.createForInStatement(left, right, body)) + return (delegate.createForStatement(init, test, update, body) if ( + 'undefined' if not 'left' in locals() else typeof(left)) == "undefined" + else delegate.createForInStatement(left, right, body)) + def parseContinueStatement(): label = None @@ -2144,26 +2426,23 @@ def parseContinueStatement(): if (ord(source[index]) if index < len(source) else None) == 59: lex() if not state.inIteration: - throwError(jsdict({ -}), Messages.IllegalContinue) + throwError(jsdict({}), Messages.IllegalContinue) return delegate.createContinueStatement(None) if peekLineTerminator(): if not state.inIteration: - throwError(jsdict({ -}), Messages.IllegalContinue) + throwError(jsdict({}), Messages.IllegalContinue) return delegate.createContinueStatement(None) if lookahead.type == Token.Identifier: label = parseVariableIdentifier() key = "$" + label.name if not (key in state.labelSet): - throwError(jsdict({ -}), Messages.UnknownLabel, label.name) + throwError(jsdict({}), Messages.UnknownLabel, label.name) consumeSemicolon() if (label == None) and (not state.inIteration): - throwError(jsdict({ -}), Messages.IllegalContinue) + throwError(jsdict({}), Messages.IllegalContinue) return delegate.createContinueStatement(label) + def parseBreakStatement(): label = None key = None @@ -2171,34 +2450,31 @@ def parseBreakStatement(): if (ord(source[index]) if index < len(source) else None) == 59: lex() if not (state.inIteration or state.inSwitch): - throwError(jsdict({ -}), Messages.IllegalBreak) + throwError(jsdict({}), Messages.IllegalBreak) return delegate.createBreakStatement(None) if peekLineTerminator(): if not (state.inIteration or state.inSwitch): - throwError(jsdict({ -}), Messages.IllegalBreak) + throwError(jsdict({}), Messages.IllegalBreak) return delegate.createBreakStatement(None) if lookahead.type == Token.Identifier: label = parseVariableIdentifier() key = "$" + label.name if not (key in state.labelSet): - throwError(jsdict({ -}), Messages.UnknownLabel, label.name) + throwError(jsdict({}), Messages.UnknownLabel, label.name) consumeSemicolon() if (label == None) and (not (state.inIteration or state.inSwitch)): - throwError(jsdict({ -}), Messages.IllegalBreak) + throwError(jsdict({}), Messages.IllegalBreak) return delegate.createBreakStatement(label) + def parseReturnStatement(): argument = None expectKeyword("return") if not state.inFunctionBody: - throwErrorTolerant(jsdict({ -}), Messages.IllegalReturn) + throwErrorTolerant(jsdict({}), Messages.IllegalReturn) if (ord(source[index]) if index < len(source) else None) == 32: - if isIdentifierStart((ord(source[index + 1]) if (index + 1) < len(source) else None)): + if isIdentifierStart((ord(source[index + 1]) if + (index + 1) < len(source) else None)): argument = parseExpression() consumeSemicolon() return delegate.createReturnStatement(argument) @@ -2210,12 +2486,12 @@ def parseReturnStatement(): consumeSemicolon() return delegate.createReturnStatement(argument) + def parseWithStatement(): object = None body = None if strict: - throwErrorTolerant(jsdict({ -}), Messages.StrictModeWith) + throwErrorTolerant(jsdict({}), Messages.StrictModeWith) expectKeyword("with") expect("(") object = parseExpression() @@ -2223,6 +2499,7 @@ def parseWithStatement(): body = parseStatement() return delegate.createWithStatement(object, body) + def parseSwitchCase(): test = None consequent = [] @@ -2243,6 +2520,7 @@ def parseSwitchCase(): consequent.append(statement) return delegate.markEnd(delegate.createSwitchCase(test, consequent)) + def parseSwitchStatement(): discriminant = None cases = None @@ -2267,24 +2545,24 @@ def parseSwitchStatement(): clause = parseSwitchCase() if clause.test == None: if defaultFound: - throwError(jsdict({ -}), Messages.MultipleDefaultsInSwitch) + throwError(jsdict({}), Messages.MultipleDefaultsInSwitch) defaultFound = True cases.append(clause) state.inSwitch = oldInSwitch expect("}") return delegate.createSwitchStatement(discriminant, cases) + def parseThrowStatement(): argument = None expectKeyword("throw") if peekLineTerminator(): - throwError(jsdict({ -}), Messages.NewlineAfterThrow) + throwError(jsdict({}), Messages.NewlineAfterThrow) argument = parseExpression() consumeSemicolon() return delegate.createThrowStatement(argument) + def parseCatchClause(): param = None body = None @@ -2296,12 +2574,12 @@ def parseCatchClause(): throwUnexpected(lookahead) param = parseVariableIdentifier() if strict and isRestrictedWord(param.name): - throwErrorTolerant(jsdict({ -}), Messages.StrictCatchVariable) + throwErrorTolerant(jsdict({}), Messages.StrictCatchVariable) expect(")") body = parseBlock() return delegate.markEnd(delegate.createCatchClause(param, body)) + def parseTryStatement(): block = None handlers = [] @@ -2314,15 +2592,16 @@ def parseTryStatement(): lex() finalizer = parseBlock() if (len(handlers) == 0) and (not finalizer): - throwError(jsdict({ -}), Messages.NoCatchOrFinally) + throwError(jsdict({}), Messages.NoCatchOrFinally) return delegate.createTryStatement(block, [], handlers, finalizer) + def parseDebuggerStatement(): expectKeyword("debugger") consumeSemicolon() return delegate.createDebuggerStatement() + def parseStatement(): type = lookahead.type expr = None @@ -2381,15 +2660,16 @@ def parseStatement(): lex() key = "$" + expr.name if key in state.labelSet: - throwError(jsdict({ -}), Messages.Redeclaration, "Label", expr.name) + throwError(jsdict({}), Messages.Redeclaration, "Label", expr.name) state.labelSet[key] = True labeledBody = parseStatement() del state.labelSet[key] - return delegate.markEnd(delegate.createLabeledStatement(expr, labeledBody)) + return delegate.markEnd( + delegate.createLabeledStatement(expr, labeledBody)) consumeSemicolon() return delegate.markEnd(delegate.createExpressionStatement(expr)) + def parseFunctionSourceElements(): global strict sourceElement = None @@ -2416,7 +2696,8 @@ def parseFunctionSourceElements(): if directive == "use strict": strict = True if firstRestricted: - throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral) + throwErrorTolerant(firstRestricted, + Messages.StrictOctalLiteral) else: if (not firstRestricted) and token.octal: firstRestricted = token @@ -2424,8 +2705,7 @@ def parseFunctionSourceElements(): oldInIteration = state.inIteration oldInSwitch = state.inSwitch oldInFunctionBody = state.inFunctionBody - state.labelSet = jsdict({ -}) + state.labelSet = jsdict({}) state.inIteration = False state.inSwitch = False state.inFunctionBody = True @@ -2433,7 +2713,8 @@ def parseFunctionSourceElements(): if match("}"): break sourceElement = parseSourceElement() - if ('undefined' if not 'sourceElement' in locals() else typeof(sourceElement)) == "undefined": + if ('undefined' if not 'sourceElement' in locals() else + typeof(sourceElement)) == "undefined": break sourceElements.append(sourceElement) expect("}") @@ -2443,6 +2724,7 @@ def parseFunctionSourceElements(): state.inFunctionBody = oldInFunctionBody return delegate.markEnd(delegate.createBlockStatement(sourceElements)) + def parseParams(firstRestricted=None): param = None params = [] @@ -2453,8 +2735,7 @@ def parseParams(firstRestricted=None): message = None expect("(") if not match(")"): - paramSet = jsdict({ -}) + paramSet = jsdict({}) while index < length: token = lookahead param = parseVariableIdentifier() @@ -2483,11 +2764,12 @@ def parseParams(firstRestricted=None): expect(",") expect(")") return jsdict({ -"params": params, -"stricted": stricted, -"firstRestricted": firstRestricted, -"message": message, -}) + "params": params, + "stricted": stricted, + "firstRestricted": firstRestricted, + "message": message, + }) + def parseFunctionDeclaration(): global strict @@ -2528,7 +2810,9 @@ def parseFunctionDeclaration(): if strict and stricted: throwErrorTolerant(stricted, message) strict = previousStrict - return delegate.markEnd(delegate.createFunctionDeclaration(id, params, [], body)) + return delegate.markEnd( + delegate.createFunctionDeclaration(id, params, [], body)) + def parseFunctionExpression(): global strict @@ -2569,7 +2853,9 @@ def parseFunctionExpression(): if strict and stricted: throwErrorTolerant(stricted, message) strict = previousStrict - return delegate.markEnd(delegate.createFunctionExpression(id, params, [], body)) + return delegate.markEnd( + delegate.createFunctionExpression(id, params, [], body)) + def parseSourceElement(): if lookahead.type == Token.Keyword: @@ -2584,6 +2870,7 @@ def parseSourceElement(): if lookahead.type != Token.EOF: return parseStatement() + def parseSourceElements(): global strict sourceElement = None @@ -2603,17 +2890,20 @@ def parseSourceElements(): if directive == "use strict": strict = True if firstRestricted: - throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral) + throwErrorTolerant(firstRestricted, + Messages.StrictOctalLiteral) else: if (not firstRestricted) and token.octal: firstRestricted = token while index < length: sourceElement = parseSourceElement() - if ('undefined' if not 'sourceElement' in locals() else typeof(sourceElement)) == "undefined": + if ('undefined' if not 'sourceElement' in locals() else + typeof(sourceElement)) == "undefined": break sourceElements.append(sourceElement) return sourceElements + def parseProgram(): global strict body = None @@ -2624,6 +2914,7 @@ def parseProgram(): body = parseSourceElements() return delegate.markEnd(delegate.createProgram(body)) + def collectToken(): start = None loc = None @@ -2633,27 +2924,30 @@ def collectToken(): skipComment() start = index loc = jsdict({ -"start": jsdict({ -"line": lineNumber, -"column": index - lineStart, -}), -}) + "start": + jsdict({ + "line": lineNumber, + "column": index - lineStart, + }), + }) token = extra.advance() loc.end = jsdict({ -"line": lineNumber, -"column": index - lineStart, -}) + "line": lineNumber, + "column": index - lineStart, + }) if token.type != Token.EOF: range = [token.range[0], token.range[1]] value = source[token.range[0]:token.range[1]] - extra.tokens.append(jsdict({ -"type": TokenName[token.type], -"value": value, -"range": range, -"loc": loc, -})) + extra.tokens.append( + jsdict({ + "type": TokenName[token.type], + "value": value, + "range": range, + "loc": loc, + })) return token + def collectRegex(): pos = None loc = None @@ -2662,30 +2956,33 @@ def collectRegex(): skipComment() pos = index loc = jsdict({ -"start": jsdict({ -"line": lineNumber, -"column": index - lineStart, -}), -}) + "start": + jsdict({ + "line": lineNumber, + "column": index - lineStart, + }), + }) regex = extra.scanRegExp() loc.end = jsdict({ -"line": lineNumber, -"column": index - lineStart, -}) + "line": lineNumber, + "column": index - lineStart, + }) if not extra.tokenize: if len(extra.tokens) > 0: token = extra.tokens[len(extra.tokens) - 1] if (token.range[0] == pos) and (token.type == "Punctuator"): if (token.value == "/") or (token.value == "/="): extra.tokens.pop() - extra.tokens.append(jsdict({ -"type": "RegularExpression", -"value": regex.literal, -"range": [pos, index], -"loc": loc, -})) + extra.tokens.append( + jsdict({ + "type": "RegularExpression", + "value": regex.literal, + "range": [pos, index], + "loc": loc, + })) return regex + def filterTokenLocation(): i = None entry = None @@ -2697,9 +2994,9 @@ def filterTokenLocation(): break entry = extra.tokens[i] token = jsdict({ -"type": entry.type, -"value": entry.value, -}) + "type": entry.type, + "value": entry.value, + }) if extra.range: token.range = entry.range if extra.loc: @@ -2708,51 +3005,60 @@ def filterTokenLocation(): i += 1 extra.tokens = tokens + class LocationMarker(object): def __init__(self=None): self.marker = [index, lineNumber, index - lineStart, 0, 0, 0] - + def end(self=None): self.marker[3] = index self.marker[4] = lineNumber self.marker[5] = index - lineStart - + def apply(self=None, node=None): if extra.range: node.range = [self.marker[0], self.marker[3]] if extra.loc: node.loc = jsdict({ -"start": jsdict({ -"line": self.marker[1], -"column": self.marker[2], -}), -"end": jsdict({ -"line": self.marker[4], -"column": self.marker[5], -}), -}) + "start": + jsdict({ + "line": self.marker[1], + "column": self.marker[2], + }), + "end": + jsdict({ + "line": self.marker[4], + "column": self.marker[5], + }), + }) node = delegate.postProcess(node) - + + def createLocationMarker(): if (not extra.loc) and (not extra.range): return None skipComment() return LocationMarker() + def patch(): global advance, scanRegExp - if ('undefined' if not ('tokens' in extra) else typeof(extra.tokens)) != "undefined": + if ('undefined' if not ('tokens' in extra) else typeof( + extra.tokens)) != "undefined": extra.advance = advance extra.scanRegExp = scanRegExp advance = collectToken scanRegExp = collectRegex + def unpatch(): global advance, scanRegExp - if ('undefined' if not ('scanRegExp' in extra) else typeof(extra.scanRegExp)) == "function": + if ('undefined' if not ('scanRegExp' in extra) else typeof( + extra.scanRegExp)) == "function": advance = extra.advance scanRegExp = extra.scanRegExp + def tokenize(code, **options): global delegate, source, index, lineNumber, lineStart, length, lookahead, state, extra options = jsdict(options) @@ -2760,7 +3066,8 @@ def tokenize(code, **options): token = None tokens = None toString = str - if (('undefined' if not 'code' in locals() else typeof(code)) != "string") and (not isinstance(code, str)): + if (('undefined' if not 'code' in locals() else typeof(code)) != + "string") and (not isinstance(code, str)): code = toString(code) delegate = SyntaxTreeDelegate source = code @@ -2770,28 +3077,29 @@ def tokenize(code, **options): length = len(source) lookahead = None state = jsdict({ -"allowIn": True, -"labelSet": jsdict({ -}), -"inFunctionBody": False, -"inIteration": False, -"inSwitch": False, -"lastCommentStart": -1, -}) - extra = jsdict({ -}) - options = options or jsdict({ -}) + "allowIn": True, + "labelSet": jsdict({}), + "inFunctionBody": False, + "inIteration": False, + "inSwitch": False, + "lastCommentStart": -1, + }) + extra = jsdict({}) + options = options or jsdict({}) options.tokens = True extra.tokens = [] extra.tokenize = True extra.openParenToken = -1 extra.openCurlyToken = -1 - extra.range = (('undefined' if not ('range' in options) else typeof(options.range)) == "boolean") and options.range - extra.loc = (('undefined' if not ('loc' in options) else typeof(options.loc)) == "boolean") and options.loc - if (('undefined' if not ('comment' in options) else typeof(options.comment)) == "boolean") and options.comment: + extra.range = (('undefined' if not ('range' in options) else typeof( + options.range)) == "boolean") and options.range + extra.loc = (('undefined' if not ('loc' in options) else typeof( + options.loc)) == "boolean") and options.loc + if (('undefined' if not ('comment' in options) else typeof( + options.comment)) == "boolean") and options.comment: extra.comments = [] - if (('undefined' if not ('tolerant' in options) else typeof(options.tolerant)) == "boolean") and options.tolerant: + if (('undefined' if not ('tolerant' in options) else typeof( + options.tolerant)) == "boolean") and options.tolerant: extra.errors = [] if length > 0: if (typeof(source[0])) == "undefined": @@ -2812,28 +3120,31 @@ def tokenize(code, **options): extra.errors.append(lexError) break else: - raise + raise filterTokenLocation() tokens = extra.tokens - if ('undefined' if not ('comments' in extra) else typeof(extra.comments)) != "undefined": + if ('undefined' if not ('comments' in extra) else typeof( + extra.comments)) != "undefined": tokens.comments = extra.comments - if ('undefined' if not ('errors' in extra) else typeof(extra.errors)) != "undefined": + if ('undefined' if not ('errors' in extra) else typeof( + extra.errors)) != "undefined": tokens.errors = extra.errors except Exception as e: - raise + raise finally: unpatch() - extra = jsdict({ -}) + extra = jsdict({}) return tokens + def parse(code, **options): global delegate, source, index, lineNumber, lineStart, length, lookahead, state, extra options = jsdict(options) program = None toString = None toString = str - if (('undefined' if not 'code' in locals() else typeof(code)) != "string") and (not isinstance(code, str)): + if (('undefined' if not 'code' in locals() else typeof(code)) != + "string") and (not isinstance(code, str)): code = toString(code) delegate = SyntaxTreeDelegate source = code @@ -2843,27 +3154,32 @@ def parse(code, **options): length = len(source) lookahead = None state = jsdict({ -"allowIn": True, -"labelSet": jsdict({ -}), -"inFunctionBody": False, -"inIteration": False, -"inSwitch": False, -"lastCommentStart": -1, -"markerStack": [], -}) - extra = jsdict({ -}) - if ('undefined' if not 'options' in locals() else typeof(options)) != "undefined": - extra.range = (('undefined' if not ('range' in options) else typeof(options.range)) == "boolean") and options.range - extra.loc = (('undefined' if not ('loc' in options) else typeof(options.loc)) == "boolean") and options.loc - if (extra.loc and (options.source != None)) and (options.source != undefined): + "allowIn": True, + "labelSet": jsdict({}), + "inFunctionBody": False, + "inIteration": False, + "inSwitch": False, + "lastCommentStart": -1, + "markerStack": [], + }) + extra = jsdict({}) + if ('undefined' + if not 'options' in locals() else typeof(options)) != "undefined": + extra.range = (('undefined' if not ('range' in options) else typeof( + options.range)) == "boolean") and options.range + extra.loc = (('undefined' if not ('loc' in options) else typeof( + options.loc)) == "boolean") and options.loc + if (extra.loc and + (options.source != None)) and (options.source != undefined): extra.source = toString(options.source) - if (('undefined' if not ('tokens' in options) else typeof(options.tokens)) == "boolean") and options.tokens: + if (('undefined' if not ('tokens' in options) else typeof( + options.tokens)) == "boolean") and options.tokens: extra.tokens = [] - if (('undefined' if not ('comment' in options) else typeof(options.comment)) == "boolean") and options.comment: + if (('undefined' if not ('comment' in options) else typeof( + options.comment)) == "boolean") and options.comment: extra.comments = [] - if (('undefined' if not ('tolerant' in options) else typeof(options.tolerant)) == "boolean") and options.tolerant: + if (('undefined' if not ('tolerant' in options) else typeof( + options.tolerant)) == "boolean") and options.tolerant: extra.errors = [] if length > 0: if (typeof(source[0])) == "undefined": @@ -2872,20 +3188,22 @@ def parse(code, **options): patch() try: program = parseProgram() - if ('undefined' if not ('comments' in extra) else typeof(extra.comments)) != "undefined": + if ('undefined' if not ('comments' in extra) else typeof( + extra.comments)) != "undefined": program.comments = extra.comments - if ('undefined' if not ('tokens' in extra) else typeof(extra.tokens)) != "undefined": + if ('undefined' if not ('tokens' in extra) else typeof( + extra.tokens)) != "undefined": filterTokenLocation() program.tokens = extra.tokens - if ('undefined' if not ('errors' in extra) else typeof(extra.errors)) != "undefined": + if ('undefined' if not ('errors' in extra) else typeof( + extra.errors)) != "undefined": program.errors = extra.errors except Exception as e: - raise + raise finally: unpatch() - extra = jsdict({ -}) + extra = jsdict({}) return program -parse('var = 490 \n a=4;') \ No newline at end of file +parse('var = 490 \n a=4;') diff --git a/js2py/legecy_translators/objects.py b/js2py/legecy_translators/objects.py index a05f77bf..2abda3e8 100644 --- a/js2py/legecy_translators/objects.py +++ b/js2py/legecy_translators/objects.py @@ -5,13 +5,15 @@ ARRAY_LVAL = 'PyJsLvalArray%d_' from utils import * from jsparser import * -from nodevisitor import exp_translator +from nodevisitor import exp_translator import functions from flow import KEYWORD_METHODS -def FUNC_TRANSLATOR(*a):# stupid import system in python + +def FUNC_TRANSLATOR(*a): # stupid import system in python raise RuntimeError('Remember to set func translator. Thank you.') + def set_func_translator(ftrans): # stupid stupid Python or Peter global FUNC_TRANSLATOR @@ -24,11 +26,15 @@ def is_empty_object(n, last): return False # seems to be but can be empty code last = last.strip() - markers = {')', ';',} + markers = { + ')', + ';', + } if not last or last[-1] in markers: return False return True + # todo refine this function def is_object(n, last): """n may be the inside of block or object. @@ -38,19 +44,21 @@ def is_object(n, last): if not n.strip(): return False #Object contains lines of code so it cant be an object - if len(argsplit(n, ';'))>1: + if len(argsplit(n, ';')) > 1: return False cands = argsplit(n, ',') if not cands[-1].strip(): - return True # {xxxx,} empty after last , it must be an object + return True # {xxxx,} empty after last , it must be an object for cand in cands: cand = cand.strip() # separate each candidate element at : in dict and check whether they are correct... kv = argsplit(cand, ':') - if len(kv) > 2: # set the len of kv to 2 because of this stupid : expression - kv = kv[0],':'.join(kv[1:]) + if len( + kv + ) > 2: # set the len of kv to 2 because of this stupid : expression + kv = kv[0], ':'.join(kv[1:]) - if len(kv)==2: + if len(kv) == 2: # key value pair, check whether not label or ?: k, v = kv if not is_lval(k.strip()): @@ -59,11 +67,12 @@ def is_object(n, last): if v.startswith('function'): continue #will fail on label... {xxx: while {}} - if v[0]=='{': # value cant be a code block + if v[0] == '{': # value cant be a code block return False for e in KEYWORD_METHODS: # if v starts with any statement then return false - if v.startswith(e) and len(e) 2 @@ -182,54 +195,56 @@ def translate_object(obj, lval, obj_count=1, arr_count=1): res.append('%s:%s' % (key, value)) res = '%s = Js({%s})\n' % (lval, ','.join(res)) + gsetters_after # translate all the nested objects (including removed earlier functions) - for nested_name, nested_info in inline.iteritems(): # functions + for nested_name, nested_info in inline.iteritems(): # functions nested_block, nested_args = nested_info new_def = FUNC_TRANSLATOR(nested_name, nested_block, nested_args) res = new_def + res - for lval, obj in obj_rep.iteritems(): #objects - new_def, obj_count, arr_count = translate_object(obj, lval, obj_count, arr_count) + for lval, obj in obj_rep.iteritems(): #objects + new_def, obj_count, arr_count = translate_object( + obj, lval, obj_count, arr_count) # add object definition BEFORE array definition res = new_def + res - for lval, obj in arr_rep.iteritems(): # arrays - new_def, obj_count, arr_count = translate_array(obj, lval, obj_count, arr_count) + for lval, obj in arr_rep.iteritems(): # arrays + new_def, obj_count, arr_count = translate_array( + obj, lval, obj_count, arr_count) # add object definition BEFORE array definition res = new_def + res return res, obj_count, arr_count - def translate_setter(lval, setter): func = 'function' + setter[3:] try: _, data, _ = functions.remove_functions(func) - if not data or len(data)>1: + if not data or len(data) > 1: raise Exception() except: - raise SyntaxError('Could not parse setter: '+setter) + raise SyntaxError('Could not parse setter: ' + setter) prop = data.keys()[0] body, args = data[prop] - if len(args)!=1: #setter must have exactly 1 argument + if len(args) != 1: #setter must have exactly 1 argument raise SyntaxError('Invalid setter. It must take exactly 1 argument.') # now messy part res = FUNC_TRANSLATOR('setter', body, args) - res += "%s.define_own_property(%s, {'set': setter})\n"%(lval, repr(prop)) + res += "%s.define_own_property(%s, {'set': setter})\n" % (lval, repr(prop)) return res + def translate_getter(lval, getter): func = 'function' + getter[3:] try: _, data, _ = functions.remove_functions(func) - if not data or len(data)>1: + if not data or len(data) > 1: raise Exception() except: - raise SyntaxError('Could not parse getter: '+getter) + raise SyntaxError('Could not parse getter: ' + getter) prop = data.keys()[0] body, args = data[prop] - if len(args)!=0: #setter must have exactly 0 argument + if len(args) != 0: #setter must have exactly 0 argument raise SyntaxError('Invalid getter. It must take exactly 0 argument.') # now messy part res = FUNC_TRANSLATOR('getter', body, args) - res += "%s.define_own_property(%s, {'get': setter})\n"%(lval, repr(prop)) + res += "%s.define_own_property(%s, {'get': setter})\n" % (lval, repr(prop)) return res @@ -261,27 +276,25 @@ def translate_array(array, lval, obj_count=1, arr_count=1): new_def = FUNC_TRANSLATOR(nested_name, nested_block, nested_args) arr = new_def + arr for lval, obj in obj_rep.iteritems(): - new_def, obj_count, arr_count = translate_object(obj, lval, obj_count, arr_count) + new_def, obj_count, arr_count = translate_object( + obj, lval, obj_count, arr_count) # add object definition BEFORE array definition arr = new_def + arr for lval, obj in arr_rep.iteritems(): - new_def, obj_count, arr_count = translate_array(obj, lval, obj_count, arr_count) + new_def, obj_count, arr_count = translate_array( + obj, lval, obj_count, arr_count) # add object definition BEFORE array definition arr = new_def + arr return arr, obj_count, arr_count - - - -if __name__=='__main__': +if __name__ == '__main__': test = 'a = {404:{494:19}}; b = 303; if () {f={:}; { }}' - #print remove_objects(test) #print list(bracket_split(' {}')) print - print remove_arrays('typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""], [][[5][5]])[1].toLowerCase()])') - print is_object('', ')') - - + print remove_arrays( + 'typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""], [][[5][5]])[1].toLowerCase()])' + ) + print is_object('', ')') diff --git a/js2py/legecy_translators/translator.py b/js2py/legecy_translators/translator.py index 1994ab6e..3573f06c 100644 --- a/js2py/legecy_translators/translator.py +++ b/js2py/legecy_translators/translator.py @@ -7,7 +7,6 @@ TOP_GLOBAL = '''from js2py.pyjs import *\nvar = Scope( JS_BUILTINS )\nset_global_object(var)\n''' - def translate_js(js, top=TOP_GLOBAL): """js has to be a javascript source code. returns equivalent python code.""" @@ -38,34 +37,39 @@ def translate_js(js, top=TOP_GLOBAL): #print len(hoisted) , 'HH'*40 for nested_name, nested_info in hoisted.iteritems(): nested_block, nested_args = nested_info - new_code = translate_func('PyJsLvalTempHoisted', nested_block, nested_args) - new_code += 'PyJsLvalTempHoisted.func_name = %s\n' %repr(nested_name) - defs += new_code +'\nvar.put(%s, PyJsLvalTempHoisted)\n' % repr(nested_name) + new_code = translate_func('PyJsLvalTempHoisted', nested_block, + nested_args) + new_code += 'PyJsLvalTempHoisted.func_name = %s\n' % repr(nested_name) + defs += new_code + '\nvar.put(%s, PyJsLvalTempHoisted)\n' % repr( + nested_name) #defs += '# Everting ready!\n' # inline functions recovery for nested_name, nested_info in inline.iteritems(): nested_block, nested_args = nested_info new_code = translate_func(nested_name, nested_block, nested_args) - py_seed = inject_before_lval(py_seed, nested_name.split('@')[0], new_code) + py_seed = inject_before_lval(py_seed, + nested_name.split('@')[0], new_code) # add hoisted definitiond - they have literals that have to be recovered py_seed = defs + py_seed #Recover arrays for arr_lval, arr_code in arrays.iteritems(): - translation, obj_count, arr_count = translate_array(arr_code, arr_lval, obj_count, arr_count) + translation, obj_count, arr_count = translate_array( + arr_code, arr_lval, obj_count, arr_count) py_seed = inject_before_lval(py_seed, arr_lval, translation) #Recover objects for obj_lval, obj_code in objects.iteritems(): - translation, obj_count, arr_count = translate_object(obj_code, obj_lval, obj_count, arr_count) + translation, obj_count, arr_count = translate_object( + obj_code, obj_lval, obj_count, arr_count) py_seed = inject_before_lval(py_seed, obj_lval, translation) - #Recover constants py_code = recover_constants(py_seed, constants) return top + py_code + def translate_func(name, block, args): """Translates functions and all nested functions to Python code. name - name of that function (global functions will be available under var while @@ -76,12 +80,12 @@ def translate_func(name, block, args): real_name = '' if inline: name, real_name = name.split('@') - arglist = ', '.join(args) +', ' if args else '' + arglist = ', '.join(args) + ', ' if args else '' code = '@Js\ndef %s(%sthis, arguments, var=var):\n' % (name, arglist) # register local variables - scope = "'this':this, 'arguments':arguments" #it will be a simple dictionary + scope = "'this':this, 'arguments':arguments" #it will be a simple dictionary for arg in args: - scope += ', %s:%s' %(repr(arg), arg) + scope += ', %s:%s' % (repr(arg), arg) if real_name: scope += ', %s:%s' % (repr(real_name), name) code += indent('var = Scope({%s}, var)\n' % scope) @@ -90,26 +94,30 @@ def translate_func(name, block, args): #register variables declared with var and names of hoisted functions. to_register += nested_hoisted.keys() if to_register: - code += indent('var.registers(%s)\n'% str(to_register)) + code += indent('var.registers(%s)\n' % str(to_register)) for nested_name, info in nested_hoisted.iteritems(): nested_block, nested_args = info - new_code = translate_func('PyJsLvalTempHoisted', nested_block, nested_args) + new_code = translate_func('PyJsLvalTempHoisted', nested_block, + nested_args) # Now put definition of hoisted function on the top code += indent(new_code) - code += indent('PyJsLvalTempHoisted.func_name = %s\n' %repr(nested_name)) - code += indent('var.put(%s, PyJsLvalTempHoisted)\n' % repr(nested_name)) + code += indent( + 'PyJsLvalTempHoisted.func_name = %s\n' % repr(nested_name)) + code += indent( + 'var.put(%s, PyJsLvalTempHoisted)\n' % repr(nested_name)) for nested_name, info in nested_inline.iteritems(): nested_block, nested_args = info new_code = translate_func(nested_name, nested_block, nested_args) # Inject definitions of inline functions just before usage # nested inline names have this format : LVAL_NAME@REAL_NAME - py_code = inject_before_lval(py_code, nested_name.split('@')[0], new_code) + py_code = inject_before_lval(py_code, + nested_name.split('@')[0], new_code) if py_code.strip(): code += indent(py_code) return code -set_func_translator(translate_func) +set_func_translator(translate_func) #print inject_before_lval(' chuj\n moj\n lval\nelse\n', 'lval', 'siema\njestem piter\n') import time @@ -123,7 +131,7 @@ def translate_func(name, block, args): """ -SANDBOX =''' +SANDBOX = ''' import traceback try: %s @@ -132,12 +140,12 @@ def translate_func(name, block, args): print raw_input('Press Enter to quit') ''' -if __name__=='__main__': +if __name__ == '__main__': # test with jq if works then it really works :) #with open('jq.js', 'r') as f: - #jq = f.read() + #jq = f.read() #res = translate_js(jq) res = translate_js(t) - dbg(SANDBOX% indent(res)) - print 'Done' \ No newline at end of file + dbg(SANDBOX % indent(res)) + print 'Done' diff --git a/js2py/legecy_translators/utils.py b/js2py/legecy_translators/utils.py index f5bd7890..b14e13d8 100644 --- a/js2py/legecy_translators/utils.py +++ b/js2py/legecy_translators/utils.py @@ -2,6 +2,7 @@ import unicodedata from collections import defaultdict + def is_lval(t): """Does not chceck whether t is not resticted or internal""" if not t: @@ -11,6 +12,7 @@ def is_lval(t): return False return all(e in IDENTIFIER_PART for e in i) + def is_valid_lval(t): """Checks whether t is valid JS identifier name (no keyword like var, function, if etc) Also returns false on internal""" @@ -22,21 +24,23 @@ def is_valid_lval(t): def is_plval(t): return t.startswith('PyJsLval') + def is_marker(t): return t.startswith('PyJsMarker') or t.startswith('PyJsConstant') + def is_internal(t): - return is_plval(t) or is_marker(t) or t=='var' # var is a scope var + return is_plval(t) or is_marker(t) or t == 'var' # var is a scope var + def is_property_accessor(t): return '[' in t or '.' in t + def is_reserved(t): return t in RESERVED_NAMES - - #http://stackoverflow.com/questions/14245893/efficiently-list-all-characters-in-a-given-unicode-category BOM = u'\uFEFF' ZWJ = u'\u200D' @@ -55,26 +59,33 @@ def is_reserved(t): for c in map(unichr, range(sys.maxunicode + 1)): U_CATEGORIES[unicodedata.category(c)].append(c) -UNICODE_LETTER = set(U_CATEGORIES['Lu']+U_CATEGORIES['Ll']+ - U_CATEGORIES['Lt']+U_CATEGORIES['Lm']+ - U_CATEGORIES['Lo']+U_CATEGORIES['Nl']) -UNICODE_COMBINING_MARK = set(U_CATEGORIES['Mn']+U_CATEGORIES['Mc']) +UNICODE_LETTER = set(U_CATEGORIES['Lu'] + U_CATEGORIES['Ll'] + + U_CATEGORIES['Lt'] + U_CATEGORIES['Lm'] + + U_CATEGORIES['Lo'] + U_CATEGORIES['Nl']) +UNICODE_COMBINING_MARK = set(U_CATEGORIES['Mn'] + U_CATEGORIES['Mc']) UNICODE_DIGIT = set(U_CATEGORIES['Nd']) UNICODE_CONNECTOR_PUNCTUATION = set(U_CATEGORIES['Pc']) -IDENTIFIER_START = UNICODE_LETTER.union({'$','_'}) # and some fucking unicode escape sequence -IDENTIFIER_PART = IDENTIFIER_START.union(UNICODE_COMBINING_MARK).union(UNICODE_DIGIT).union(UNICODE_CONNECTOR_PUNCTUATION).union({ZWJ, ZWNJ}) +IDENTIFIER_START = UNICODE_LETTER.union( + {'$', '_'}) # and some fucking unicode escape sequence +IDENTIFIER_PART = IDENTIFIER_START.union(UNICODE_COMBINING_MARK).union( + UNICODE_DIGIT).union(UNICODE_CONNECTOR_PUNCTUATION).union({ZWJ, ZWNJ}) USP = U_CATEGORIES['Zs'] -KEYWORD = {'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', - 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', - 'switch', 'while', 'debugger', 'function', 'this', 'with', 'default', - 'if', 'throw', 'delete', 'in', 'try'} - -FUTURE_RESERVED_WORD = {'class', 'enum', 'extends', 'super', 'const', 'export', 'import'} -RESERVED_NAMES = KEYWORD.union(FUTURE_RESERVED_WORD).union({'null', 'false', 'true'}) +KEYWORD = { + 'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', + 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while', + 'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', + 'in', 'try' +} + +FUTURE_RESERVED_WORD = { + 'class', 'enum', 'extends', 'super', 'const', 'export', 'import' +} +RESERVED_NAMES = KEYWORD.union(FUTURE_RESERVED_WORD).union( + {'null', 'false', 'true'}) WHITE = {TAB, VT, FF, SP, NBSP, BOM}.union(USP) LINE_TERMINATOR = {LF, CR, LS, PS} LLINE_TERMINATOR = list(LINE_TERMINATOR) -x = ''.join(WHITE)+''.join(LINE_TERMINATOR) +x = ''.join(WHITE) + ''.join(LINE_TERMINATOR) SPACE = WHITE.union(LINE_TERMINATOR) -LINE_TERMINATOR_SEQUENCE = LINE_TERMINATOR.union({CR+LF}) \ No newline at end of file +LINE_TERMINATOR_SEQUENCE = LINE_TERMINATOR.union({CR + LF}) diff --git a/js2py/node_import.py b/js2py/node_import.py index 30dc1b60..a49a1f51 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -5,14 +5,23 @@ 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' % repr(DIRNAME), shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules' + 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' + % 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')) { @@ -37,12 +46,14 @@ def _init(): ''' + def require(module_name, include_polyfill=False, update=False): assert isinstance(module_name, str), 'module_name must be a string!' py_name = module_name.replace('-', '_') - module_filename = '%s.py'%py_name + module_filename = '%s.py' % py_name var_name = py_name.rpartition('/')[-1] - if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH, module_filename)) or update: + 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' @@ -58,35 +69,45 @@ def require(module_name, include_polyfill=False, update=False): 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 + 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) {fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"''' % (in_file_name, out_file_name), + '''node -e "(require('browserify')('./%s').bundle(function (err,data) {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' + ) == 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: + 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)) js_code += GET_FROM_GLOBALS_FUNC - js_code += ';var %s = getFromGlobals(%s);%s' % (var_name, repr(module_name), var_name) + 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)) + 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: + 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: + with codecs.open( + os.path.join(PY_NODE_MODULES_PATH, module_filename), "r", + "utf-8") as f: py_code = f.read() context = {} - exec(py_code, context) + exec (py_code, context) return context['var'][var_name].to_py() - diff --git a/js2py/prototypes/jsarray.py b/js2py/prototypes/jsarray.py index efa4796d..d02e62b2 100644 --- a/js2py/prototypes/jsarray.py +++ b/js2py/prototypes/jsarray.py @@ -4,6 +4,7 @@ xrange = range import functools + def to_arr(this): """Returns Python array from Js array""" return [this.get(str(e)) for e in xrange(len(this))] @@ -11,14 +12,17 @@ def to_arr(this): ARR_STACK = set({}) + class ArrayPrototype: def toString(): # this function is wrong but I will leave it here fore debugging purposes. func = this.get('join') if not func.is_callable(): + @this.Js def func(): - return '[object %s]'%this.Class + return '[object %s]' % this.Class + return func.call(this, ()) def toLocaleString(): @@ -36,7 +40,10 @@ def toLocaleString(): cand = element.to_object() str_func = element.get('toLocaleString') if not str_func.is_callable(): - raise this.MakeError('TypeError', 'toLocaleString method of item at index %d is not callable'%i) + raise this.MakeError( + 'TypeError', + 'toLocaleString method of item at index %d is not callable' + % i) res.append(element.callprop('toLocaleString').value) return ','.join(res) @@ -47,24 +54,25 @@ def concat(): items.extend(to_arr(arguments)) n = 0 for E in items: - if E.Class=='Array': + if E.Class == 'Array': k = 0 e_len = len(E) - while k (arr_len - actual_delete_count + items_len): - array.delete(str(k-1)) + array.delete(str(k - 1)) k -= 1 # 13 - elif items_len>actual_delete_count: + elif items_len > actual_delete_count: k = arr_len - actual_delete_count - while k>actual_start: + while k > actual_start: fr = str(k + actual_delete_count - 1) to = str(k + items_len - 1) if array.has_property(fr): @@ -252,7 +263,7 @@ def indexOf(searchElement): arr_len = array.get('length').to_uint32() if arr_len == 0: return -1 - if len(arguments)>1: + if len(arguments) > 1: n = arguments[1].to_int() else: n = 0 @@ -277,12 +288,12 @@ def lastIndexOf(searchElement): arr_len = array.get('length').to_uint32() if arr_len == 0: return -1 - if len(arguments)>1: + if len(arguments) > 1: n = arguments[1].to_int() else: n = arr_len - 1 if n >= 0: - k = min(n, arr_len-1) + k = min(n, arr_len - 1) else: k = arr_len - abs(n) while k >= 0: @@ -293,7 +304,6 @@ def lastIndexOf(searchElement): k -= 1 return -1 - def every(callbackfn): array = this.to_object() arr_len = array.get('length').to_uint32() @@ -301,15 +311,15 @@ def every(callbackfn): raise this.MakeError('TypeError', 'callbackfn must be a function') T = arguments[1] k = 0 - while k1: # initial value present + if len(arguments) > 1: # initial value present accumulator = arguments[1] else: kPresent = False - while not kPresent and k1: # initial value present + if len(arguments) > 1: # initial value present accumulator = arguments[1] else: kPresent = False - while not kPresent and k>=0: + while not kPresent and k >= 0: kPresent = array.has_property(str(k)) if kPresent: accumulator = array.get(str(k)) k -= 1 if not kPresent: - raise this.MakeError('TypeError', 'Reduce of empty array with no initial value') - while k>=0: + raise this.MakeError( + 'TypeError', 'Reduce of empty array with no initial value') + while k >= 0: if array.has_property(str(k)): kValue = array.get(str(k)) - accumulator = callbackfn.call(this.undefined, (accumulator, kValue, this.Js(k), array)) + accumulator = callbackfn.call( + this.undefined, (accumulator, kValue, this.Js(k), array)) k -= 1 return accumulator @@ -447,12 +469,8 @@ def sort_compare(a, b, comp): res = comp.call(a.undefined, (a, b)) return res.to_int() x, y = a.to_string(), b.to_string() - if xy: + elif x > y: return 1 return 0 - - - - diff --git a/js2py/prototypes/jsarraybuffer.py b/js2py/prototypes/jsarraybuffer.py index 5c7cf348..7bcd00fb 100644 --- a/js2py/prototypes/jsarraybuffer.py +++ b/js2py/prototypes/jsarraybuffer.py @@ -6,6 +6,7 @@ xrange = range import functools + def to_arr(this): """Returns Python array from Js array""" return [this.get(str(e)) for e in xrange(len(this))] @@ -13,5 +14,6 @@ def to_arr(this): ARR_STACK = set({}) + class ArrayBufferPrototype: pass diff --git a/js2py/prototypes/jsboolean.py b/js2py/prototypes/jsboolean.py index af048953..2cacd32d 100644 --- a/js2py/prototypes/jsboolean.py +++ b/js2py/prototypes/jsboolean.py @@ -1,16 +1,10 @@ - - class BooleanPrototype: def toString(): - if this.Class!='Boolean': + if this.Class != 'Boolean': raise this.Js(TypeError)('this must be a boolean') return 'true' if this.value else 'false' def valueOf(): - if this.Class!='Boolean': + if this.Class != 'Boolean': raise this.Js(TypeError)('this must be a boolean') return this.value - - - - diff --git a/js2py/prototypes/jserror.py b/js2py/prototypes/jserror.py index 934e0fc9..c488bf15 100644 --- a/js2py/prototypes/jserror.py +++ b/js2py/prototypes/jserror.py @@ -1,10 +1,10 @@ - class ErrorPrototype: def toString(): - if this.TYPE!='Object': - raise this.MakeError('TypeError', 'Error.prototype.toString called on non-object') + if this.TYPE != 'Object': + raise this.MakeError( + 'TypeError', 'Error.prototype.toString called on non-object') name = this.get('name') name = 'Error' if name.is_undefined() else name.to_string().value msg = this.get('message') msg = '' if msg.is_undefined() else msg.to_string().value - return name + (name and msg and ': ') + msg \ No newline at end of file + return name + (name and msg and ': ') + msg diff --git a/js2py/prototypes/jsfunction.py b/js2py/prototypes/jsfunction.py index 1b67db24..f9598a31 100644 --- a/js2py/prototypes/jsfunction.py +++ b/js2py/prototypes/jsfunction.py @@ -6,35 +6,35 @@ xrange = range unicode = str - # todo fix apply and bind -class FunctionPrototype: + +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 - + 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 = () + 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 = () + if len(arguments) <= 1: + args = () else: appl = arguments[1] args = tuple([appl[e] for e in xrange(len(appl))]) @@ -43,11 +43,10 @@ def apply(): def bind(thisArg): target = this if not target.is_callable(): - raise this.MakeError('Object must be callable in order to be used with bind method') + 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) - - diff --git a/js2py/prototypes/jsjson.py b/js2py/prototypes/jsjson.py index e933dd86..9f7ccbb0 100644 --- a/js2py/prototypes/jsjson.py +++ b/js2py/prototypes/jsjson.py @@ -16,7 +16,8 @@ def parse(text): try: unfiltered = json.loads(s) except: - raise this.MakeError('SyntaxError', 'Could not parse JSON string - Invalid syntax') + raise this.MakeError('SyntaxError', + 'Could not parse JSON string - Invalid syntax') unfiltered = to_js(this, unfiltered) if reviver.is_callable(): root = this.Js({'': unfiltered}) @@ -33,14 +34,14 @@ def stringify(value, replacer, space): if replacer.is_object(): if replacer.is_callable(): replacer_function = replacer - elif replacer.Class=='Array': + elif replacer.Class == 'Array': property_list = [] for e in replacer: v = replacer[e] item = this.undefined - if v._type()=='Number': + if v._type() == 'Number': item = v.to_string() - elif v._type()=='String': + elif v._type() == 'String': item = v elif v.is_object(): if v.Class in ('String', 'Number'): @@ -48,19 +49,21 @@ def stringify(value, replacer, space): if not item.is_undefined() and item.value not in property_list: property_list.append(item.value) if space.is_object(): - if space.Class=='Number': + if space.Class == 'Number': space = space.to_number() - elif space.Class=='String': + elif space.Class == 'String': space = space.to_string() - if space._type()=='Number': + if space._type() == 'Number': space = this.Js(min(10, space.to_int())) - gap = max(int(space.value), 0)* ' ' - elif space._type()=='String': + gap = max(int(space.value), 0) * ' ' + elif space._type() == 'String': gap = space.value[:10] else: gap = '' - return this.Js(Str('', this.Js({'':value}), replacer_function, property_list, gap, stack, space)) - + return this.Js( + Str('', this.Js({ + '': value + }), replacer_function, property_list, gap, stack, space)) def Str(key, holder, replacer_function, property_list, gap, stack, space): @@ -68,40 +71,42 @@ def Str(key, holder, replacer_function, property_list, gap, stack, space): if value.is_object(): to_json = value.get('toJSON') if to_json.is_callable(): - value = to_json.call(value, (key,)) + value = to_json.call(value, (key, )) if not replacer_function.is_undefined(): value = replacer_function.call(holder, (key, value)) if value.is_object(): - if value.Class=='String': + if value.Class == 'String': value = value.to_string() - elif value.Class=='Number': + elif value.Class == 'Number': value = value.to_number() - elif value.Class=='Boolean': + elif value.Class == 'Boolean': value = value.to_boolean() if value.is_null(): return 'null' - elif value.Class=='Boolean': + elif value.Class == 'Boolean': return 'true' if value.value else 'false' - elif value._type()=='String': + elif value._type() == 'String': return Quote(value) - elif value._type()=='Number': + elif value._type() == 'Number': if not value.is_infinity(): return value.to_string() return 'null' if value.is_object() and not value.is_callable(): - if value.Class=='Array': - return ja(value, stack, gap, property_list, replacer_function, space) + if value.Class == 'Array': + return ja(value, stack, gap, property_list, replacer_function, + space) else: - return jo(value, stack, gap, property_list, replacer_function, space) - return None # undefined - + return jo(value, stack, gap, property_list, replacer_function, + space) + return None # undefined def jo(value, stack, gap, property_list, replacer_function, space): global indent if value in stack: - raise value.MakeError('TypeError', 'Converting circular structure to JSON') + raise value.MakeError('TypeError', + 'Converting circular structure to JSON') stack.add(value) stepback = indent indent += gap @@ -111,9 +116,12 @@ def jo(value, stack, gap, property_list, replacer_function, space): k = [e.value for e in value] partial = [] for p in k: - str_p = value.Js(Str(p, value, replacer_function, property_list, gap, stack, space)) + str_p = value.Js( + Str(p, value, replacer_function, property_list, gap, stack, space)) if not str_p.is_undefined(): - member = json.dumps(p) + ':' + (' ' if gap else '') + str_p.value # todo not sure here - what space character? + member = json.dumps(p) + ':' + ( + ' ' if gap else + '') + str_p.value # todo not sure here - what space character? partial.append(member) if not partial: final = '{}' @@ -121,9 +129,9 @@ def jo(value, stack, gap, property_list, replacer_function, space): if not gap: final = '{%s}' % ','.join(partial) else: - sep = ',\n'+indent + sep = ',\n' + indent properties = sep.join(partial) - final = '{\n'+indent+properties+'\n'+stepback+'}' + final = '{\n' + indent + properties + '\n' + stepback + '}' stack.remove(value) indent = stepback return final @@ -132,7 +140,8 @@ def jo(value, stack, gap, property_list, replacer_function, space): def ja(value, stack, gap, property_list, replacer_function, space): global indent if value in stack: - raise value.MakeError('TypeError', 'Converting circular structure to JSON') + raise value.MakeError('TypeError', + 'Converting circular structure to JSON') stack.add(value) stepback = indent indent += gap @@ -140,7 +149,9 @@ def ja(value, stack, gap, property_list, replacer_function, space): length = len(value) for index in xrange(length): index = str(index) - str_index = value.Js(Str(index, value, replacer_function, property_list, gap, stack, space)) + str_index = value.Js( + Str(index, value, replacer_function, property_list, gap, stack, + space)) if str_index.is_undefined(): partial.append('null') else: @@ -151,31 +162,27 @@ def ja(value, stack, gap, property_list, replacer_function, space): if not gap: final = '[%s]' % ','.join(partial) else: - sep = ',\n'+indent + sep = ',\n' + indent properties = sep.join(partial) - final = '[\n'+indent +properties+'\n'+stepback+']' + final = '[\n' + indent + properties + '\n' + stepback + ']' stack.remove(value) indent = stepback return final - - - def Quote(string): return string.Js(json.dumps(string.value)) def to_js(this, d): if isinstance(d, dict): - return this.Js(dict((k,this.Js(v)) for k, v in six.iteritems(d))) + return this.Js(dict((k, this.Js(v)) for k, v in six.iteritems(d))) return this.Js(d) - def walk(holder, name, reviver): val = holder.get(name) - if val.Class=='Array': + if val.Class == 'Array': for i in xrange(len(val)): i = unicode(i) new_element = walk(val, i, reviver) @@ -193,18 +200,20 @@ def walk(holder, name, reviver): return reviver.call(holder, (name, val)) - - - - JSON = Js({}) -JSON.define_own_property('parse', {'value': Js(parse), - 'enumerable': False, - 'writable': True, - 'configurable': True}) - -JSON.define_own_property('stringify', {'value': Js(stringify), - 'enumerable': False, - 'writable': True, - 'configurable': True}) \ No newline at end of file +JSON.define_own_property( + 'parse', { + 'value': Js(parse), + 'enumerable': False, + 'writable': True, + 'configurable': True + }) + +JSON.define_own_property( + 'stringify', { + 'value': Js(stringify), + 'enumerable': False, + 'writable': True, + 'configurable': True + }) diff --git a/js2py/prototypes/jsnumber.py b/js2py/prototypes/jsnumber.py index d825c891..c9905ab0 100644 --- a/js2py/prototypes/jsnumber.py +++ b/js2py/prototypes/jsnumber.py @@ -5,35 +5,73 @@ xrange = range unicode = str - -RADIX_SYMBOLS = {0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', - 10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f', 16: 'g', 17: 'h', 18: 'i', 19: 'j', 20: 'k', - 21: 'l', 22: 'm', 23: 'n', 24: 'o', 25: 'p', 26: 'q', 27: 'r', 28: 's', 29: 't', 30: 'u', 31: 'v', - 32: 'w', 33: 'x', 34: 'y', 35: 'z'} +RADIX_SYMBOLS = { + 0: '0', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: 'a', + 11: 'b', + 12: 'c', + 13: 'd', + 14: 'e', + 15: 'f', + 16: 'g', + 17: 'h', + 18: 'i', + 19: 'j', + 20: 'k', + 21: 'l', + 22: 'm', + 23: 'n', + 24: 'o', + 25: 'p', + 26: 'q', + 27: 'r', + 28: 's', + 29: 't', + 30: 'u', + 31: 'v', + 32: 'w', + 33: 'x', + 34: 'y', + 35: 'z' +} def to_str_rep(num): if num.is_nan(): return num.Js('NaN') elif num.is_infinity(): - sign = '-' if num.value<0 else '' - return num.Js(sign+'Infinity') - elif isinstance(num.value, (long, int)) or num.value.is_integer(): # dont print .0 + sign = '-' if num.value < 0 else '' + return num.Js(sign + 'Infinity') + elif isinstance(num.value, + (long, int)) or num.value.is_integer(): # dont print .0 return num.Js(unicode(int(num.value))) - return num.Js(unicode(num.value)) # accurate enough + return num.Js(unicode(num.value)) # accurate enough class NumberPrototype: def toString(radix): - if this.Class!='Number': - raise this.MakeError('TypeError', 'Number.prototype.valueOf is not generic') + if this.Class != 'Number': + raise this.MakeError('TypeError', + 'Number.prototype.valueOf is not generic') if radix.is_undefined(): return to_str_rep(this) r = radix.to_int() - if r==10: + if r == 10: return to_str_rep(this) - if r not in xrange(2, 37): - raise this.MakeError('RangeError', 'Number.prototype.toString() radix argument must be between 2 and 36') + if r not in xrange(2, 37): + raise this.MakeError( + 'RangeError', + 'Number.prototype.toString() radix argument must be between 2 and 36' + ) num = this.to_int() if num < 0: num = -num @@ -48,53 +86,61 @@ def toString(radix): return sign + (res if res else '0') def valueOf(): - if this.Class!='Number': - raise this.MakeError('TypeError', 'Number.prototype.valueOf is not generic') + if this.Class != 'Number': + raise this.MakeError('TypeError', + 'Number.prototype.valueOf is not generic') return this.value def toLocaleString(): return this.to_string() - def toFixed (fractionDigits): - if this.Class!='Number': - raise this.MakeError('TypeError', 'Number.prototype.toFixed called on incompatible receiver') + def toFixed(fractionDigits): + if this.Class != 'Number': + raise this.MakeError( + 'TypeError', + 'Number.prototype.toFixed called on incompatible receiver') digs = fractionDigits.to_int() - if digs<0 or digs>20: - raise this.MakeError('RangeError', 'toFixed() digits argument must be between 0 and 20') + if digs < 0 or digs > 20: + raise this.MakeError( + 'RangeError', + 'toFixed() digits argument must be between 0 and 20') elif this.is_infinity(): - return 'Infinity' if this.value>0 else '-Infinity' + return 'Infinity' if this.value > 0 else '-Infinity' elif this.is_nan(): return 'NaN' - return format(this.value, '-.%df'%digs) - + return format(this.value, '-.%df' % digs) - def toExponential (fractionDigits): - if this.Class!='Number': - raise this.MakeError('TypeError', 'Number.prototype.toExponential called on incompatible receiver') + def toExponential(fractionDigits): + if this.Class != 'Number': + raise this.MakeError( + 'TypeError', + 'Number.prototype.toExponential called on incompatible receiver' + ) digs = fractionDigits.to_int() - if digs<0 or digs>20: - raise this.MakeError('RangeError', 'toFixed() digits argument must be between 0 and 20') + if digs < 0 or digs > 20: + raise this.MakeError( + 'RangeError', + 'toFixed() digits argument must be between 0 and 20') elif this.is_infinity(): - return 'Infinity' if this.value>0 else '-Infinity' + return 'Infinity' if this.value > 0 else '-Infinity' elif this.is_nan(): return 'NaN' - return format(this.value, '-.%de'%digs) + return format(this.value, '-.%de' % digs) - def toPrecision (precision): - if this.Class!='Number': - raise this.MakeError('TypeError', 'Number.prototype.toPrecision called on incompatible receiver') + def toPrecision(precision): + if this.Class != 'Number': + raise this.MakeError( + 'TypeError', + 'Number.prototype.toPrecision called on incompatible receiver') if precision.is_undefined(): return this.to_string() prec = precision.to_int() if this.is_nan(): return 'NaN' elif this.is_infinity(): - return 'Infinity' if this.value>0 else '-Infinity' + return 'Infinity' if this.value > 0 else '-Infinity' digs = prec - len(str(int(this.value))) - if digs>=0: - return format(this.value, '-.%df'%digs) + if digs >= 0: + return format(this.value, '-.%df' % digs) else: - return format(this.value, '-.%df'%(prec-1)) - - - + return format(this.value, '-.%df' % (prec - 1)) diff --git a/js2py/prototypes/jsobject.py b/js2py/prototypes/jsobject.py index 793869af..aeefe5d3 100644 --- a/js2py/prototypes/jsobject.py +++ b/js2py/prototypes/jsobject.py @@ -1,12 +1,10 @@ - class ObjectPrototype: def toString(): - return '[object %s]'%this.Class + return '[object %s]' % this.Class def valueOf(): return this.to_object() - def toLocaleString(): return this.callprop('toString') @@ -28,9 +26,3 @@ def isPrototypeOf(obj): def propertyIsEnumerable(prop): cand = this.own.get(prop.to_string().value) return cand is not None and cand.get('enumerable') - - - - - - diff --git a/js2py/prototypes/jsregexp.py b/js2py/prototypes/jsregexp.py index c14d9fda..b23f2003 100644 --- a/js2py/prototypes/jsregexp.py +++ b/js2py/prototypes/jsregexp.py @@ -1,4 +1,3 @@ - class RegExpPrototype: def toString(): flags = u'' @@ -12,20 +11,20 @@ def toString(): except: pass v = this.value if this.value else '(?:)' - return u'/%s/'%v + flags + return u'/%s/' % v + flags def test(string): return Exec(this, string) is not this.null - def exec2(string): # will be changed to exec in base.py. cant name it exec here + def exec2(string + ): # will be changed to exec in base.py. cant name it exec here return Exec(this, string) - - def Exec(this, string): - if this.Class!='RegExp': - raise this.MakeError('TypeError', 'RegExp.prototype.exec is not generic!') + if this.Class != 'RegExp': + raise this.MakeError('TypeError', + 'RegExp.prototype.exec is not generic!') string = string.to_string() length = len(string) i = this.get('lastIndex').to_int() if this.glob else 0 @@ -36,11 +35,11 @@ def Exec(this, string): return this.null matched = this.match(string.value, i) i += 1 - start, end = matched.span()#[0]+i-1, matched.span()[1]+i-1 + start, end = matched.span() #[0]+i-1, matched.span()[1]+i-1 if this.glob: this.put('lastIndex', this.Js(end)) - arr = this.Js([this.Js(e) for e in [matched.group()]+list(matched.groups())]) + arr = this.Js( + [this.Js(e) for e in [matched.group()] + list(matched.groups())]) arr.put('index', this.Js(start)) arr.put('input', string) return arr - diff --git a/js2py/prototypes/jsstring.py b/js2py/prototypes/jsstring.py index ebeed399..ee320709 100644 --- a/js2py/prototypes/jsstring.py +++ b/js2py/prototypes/jsstring.py @@ -4,69 +4,73 @@ DIGS = set('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: + while n < len(rep) - 1: char = rep[n] - if char=='$': - if rep[n+1]=='$': + if char == '$': + if rep[n + 1] == '$': res += '$' n += 2 continue - elif rep[n+1]=='`': + elif rep[n + 1] == '`': # replace with string that is BEFORE match res += source[:span[0]] n += 2 continue - elif rep[n+1]=='\'': + 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+2len(npar): - res += '$'+dig + 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 '' + res += npar[num - 1] if npar[num - 1] else '' n += 1 + len(dig) continue res += char n += 1 - if nthat: + elif s > that: return this.Js(1) return this.Js(0) def match(regexp): this.cok() s = this.to_string() - r = this.RegExp(regexp) if regexp.Class!='RegExp' else regexp + r = this.RegExp(regexp) if regexp.Class != 'RegExp' else regexp if not r.glob: return Exec(r, s) r.put('lastIndex', this.Js(0)) @@ -124,11 +128,11 @@ def match(regexp): while last_match: result = Exec(r, s) if result.is_null(): - last_match=False + last_match = False else: this_index = r.get('lastIndex').value - if this_index==previous_last_index: - r.put('lastIndex', this.Js(this_index+1)) + if this_index == previous_last_index: + r.put('lastIndex', this.Js(this_index + 1)) previous_last_index += 1 else: previous_last_index = this_index @@ -138,7 +142,6 @@ def match(regexp): return this.null return found - def replace(searchValue, replaceValue): # VERY COMPLICATED. to check again. this.cok() @@ -157,16 +160,17 @@ def replace(searchValue, replaceValue): res += s[last:e.span()[0]] if func: # prepare arguments for custom func (replaceValue) - args = (e.group(),) + e.groups() + (e.span()[1], string) + args = (e.group(), ) + e.groups() + (e.span()[1], string) # convert all types to JS args = map(this.Js, args) res += replaceValue(*args).to_string().value else: - res += replacement_template(replaceValue, s, e.span(), e.groups()) + res += replacement_template(replaceValue, s, e.span(), + e.groups()) last = e.span()[1] res += s[last:] return this.Js(res) - elif searchValue.Class=='RegExp': + elif searchValue.Class == 'RegExp': e = re.search(searchValue.pat, s) if e is None: return string @@ -175,14 +179,14 @@ def replace(searchValue, replaceValue): match = e.group() else: match = searchValue.to_string().value - ind = s.find(match) - if ind==-1: + ind = s.find(match) + if ind == -1: return string span = ind, ind + len(match) pars = () res = s[:span[0]] if func: - args = (match,) + pars + (span[1], string) + args = (match, ) + pars + (span[1], string) # convert all types to JS this_ = this args = tuple([this_.Js(x) for x in args]) @@ -195,7 +199,7 @@ def replace(searchValue, replaceValue): def search(regexp): this.cok() string = this.to_string() - if regexp.Class=='RegExp': + if regexp.Class == 'RegExp': rx = regexp else: rx = this.RegExp(regexp) @@ -209,48 +213,46 @@ def slice(start, end): s = this.to_string() start = start.to_int() length = len(s.value) - end = length if end.is_undefined() else end.to_int() + end = length if end.is_undefined() else end.to_int() #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.value[start:end] - - def split (separator, limit): + def split(separator, limit): # its a bit different that re.split! this.cok() S = this.to_string() s = S.value - lim = 2**32-1 if limit.is_undefined() else limit.to_uint32() + lim = 2**32 - 1 if limit.is_undefined() else limit.to_uint32() if not lim: return [] if separator.is_undefined(): return [s] len_s = len(s) res = [] - R = separator if separator.Class=='RegExp' else separator.to_string() + R = separator if separator.Class == 'RegExp' else separator.to_string() if not len_s: if SplitMatch(s, 0, R) is None: return [S] return [] p = q = 0 - while q!=len_s: + while q != len_s: e, cap = SplitMatch(s, q, R) - if e is None or e==p: + if e is None or e == p: q += 1 continue res.append(s[p:q]) p = q = e - if len(res)==lim: + if len(res) == lim: return res for element in cap: res.append(this.Js(element)) - if len(res)==lim: + if len(res) == lim: return res res.append(s[p:]) return res - - def substring (start, end): + def substring(start, end): this.cok() s = this.to_string().value start = start.to_int() @@ -264,13 +266,13 @@ def substr(start, length): #I hate this function and its description in specification r1 = this.to_string().value r2 = start.to_int() - r3 = 10**20 if length.is_undefined() else length.to_int() + r3 = 10**20 if length.is_undefined() else length.to_int() r4 = len(r1) - r5 = r2 if r2>=0 else max(0, r2+r4) - r6 = min(max(r3 ,0), r4 - r5) - if r6<=0: + 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] + return r1[r5:r5 + r6] def toLowerCase(): this.cok() @@ -293,15 +295,12 @@ def trim(): return this.Js(this.to_string().value.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 R.Class=='RegExp': + if R.Class == '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.value): - return q+len(R.value), () + return q + len(R.value), () return None, () - diff --git a/js2py/prototypes/jstypedarray.py b/js2py/prototypes/jstypedarray.py index ba2c33a8..43285aae 100644 --- a/js2py/prototypes/jstypedarray.py +++ b/js2py/prototypes/jstypedarray.py @@ -10,6 +10,7 @@ xrange = range import functools + def to_arr(this): """Returns Python array from Js array""" return [this.get(str(e)) for e in xrange(len(this))] @@ -17,18 +18,20 @@ def to_arr(this): ARR_STACK = set({}) -class TypedArrayPrototype: +class TypedArrayPrototype: def toString(): # this function is wrong func = this.get('join') if not func.is_callable(): + @this.Js def func(): - return '[object %s]'%this.Class + return '[object %s]' % this.Class + return func.call(this, ()) - def toLocaleString(locales=None,options=None): + def toLocaleString(locales=None, options=None): array = this.to_object() arr_len = array.get("length").to_uint32() # separator is simply a comma ',' @@ -43,7 +46,10 @@ def toLocaleString(locales=None,options=None): cand = element.to_object() str_func = element.get('toLocaleString') if not str_func.is_callable(): - raise this.MakeError('TypeError', 'toLocaleString method of item at index %d is not callable'%i) + raise this.MakeError( + 'TypeError', + 'toLocaleString method of item at index %d is not callable' + % i) res.append(element.callprop('toLocaleString').value) return ','.join(res) @@ -51,7 +57,8 @@ def join(separator): ARR_STACK.add(this) array = this.to_object() arr_len = array.get("length").to_uint32() - separator = ',' if separator.is_undefined() else separator.to_string().value + separator = ',' if separator.is_undefined() else separator.to_string( + ).value elems = [] for e in xrange(arr_len): elem = array.get(str(e)) @@ -59,13 +66,14 @@ def join(separator): s = '' else: s = elem.to_string().value - elems.append(s if not (elem.is_undefined() or elem.is_null()) else '') - res = separator.join(elems) + elems.append( + s if not (elem.is_undefined() or elem.is_null()) else '') + res = separator.join(elems) ARR_STACK.remove(this) return res def reverse(): - array = this.to_object() # my own algorithm + array = this.to_object() # my own algorithm vals = to_arr(array) has_props = [array.has_property(str(e)) for e in xrange(len(array))] vals.reverse() @@ -77,16 +85,18 @@ def reverse(): array.delete(str(i)) return array - def slice(start, end): # todo check + def slice(start, end): # todo check array = this.to_object() arr_len = array.get("length").to_uint32() relative_start = start.to_int() - k = max((arr_len + relative_start), 0) if relative_start<0 else min(relative_start, arr_len) + k = max((arr_len + relative_start), 0) if relative_start < 0 else min( + relative_start, arr_len) relative_end = arr_len if end.is_undefined() else end.to_int() - final = max((arr_len + relative_end), 0) if relative_end<0 else min(relative_end, arr_len) + final = max((arr_len + relative_end), 0) if relative_end < 0 else min( + relative_end, arr_len) res = [] n = 0 - while k1: + if len(arguments) > 1: n = arguments[1].to_int() else: n = 0 @@ -146,12 +156,12 @@ def lastIndexOf(searchElement): arr_len = array.get("length").to_uint32() if arr_len == 0: return -1 - if len(arguments)>1: + if len(arguments) > 1: n = arguments[1].to_int() else: n = arr_len - 1 if n >= 0: - k = min(n, arr_len-1) + k = min(n, arr_len - 1) else: k = arr_len - abs(n) while k >= 0: @@ -169,10 +179,11 @@ def every(callbackfn): raise this.MakeError('TypeError', 'callbackfn must be a function') T = arguments[1] k = 0 - while k1: # initial value present + if len(arguments) > 1: # initial value present accumulator = arguments[1] else: kPresent = False - while not kPresent and k1: # initial value present + if len(arguments) > 1: # initial value present accumulator = arguments[1] else: kPresent = False - while not kPresent and k>=0: + while not kPresent and k >= 0: kPresent = array.has_property(str(k)) if kPresent: accumulator = array.get(str(k)) k -= 1 if not kPresent: - raise this.MakeError('TypeError', 'Reduce of empty array with no initial value') - while k>=0: + raise this.MakeError( + 'TypeError', 'Reduce of empty array with no initial value') + while k >= 0: if array.has_property(str(k)): kValue = array.get(str(k)) - accumulator = callbackfn.call(this.undefined, (accumulator, kValue, this.Js(k), array)) + accumulator = callbackfn.call( + this.undefined, (accumulator, kValue, this.Js(k), array)) k -= 1 return accumulator + def sort_compare(a, b, comp): if a is None: if b is None: @@ -313,12 +337,8 @@ def sort_compare(a, b, comp): res = comp.call(a.undefined, (a, b)) return res.to_int() x, y = a.to_string(), b.to_string() - if xy: + elif x > y: return 1 return 0 - - - - diff --git a/js2py/py_node_modules/__init__.py b/js2py/py_node_modules/__init__.py index 3a6e544e..03b73026 100644 --- a/js2py/py_node_modules/__init__.py +++ b/js2py/py_node_modules/__init__.py @@ -1 +1 @@ -"""this package contains all the npm modules translated by js2py via node import""" +"""this package contains all the npm modules translated by js2py via node import""" \ No newline at end of file diff --git a/js2py/pyjs.py b/js2py/pyjs.py index 55b1d606..98e106a0 100644 --- a/js2py/pyjs.py +++ b/js2py/pyjs.py @@ -24,28 +24,57 @@ from .host.jsfunctions import parseFloat, parseInt, isFinite, \ isNaN, escape, unescape, encodeURI, decodeURI, encodeURIComponent, decodeURIComponent - # Now we have all the necessary items to create global environment for script -__all__ = ['Js', 'PyJsComma', 'PyJsStrictEq', 'PyJsStrictNeq', - 'PyJsException', 'PyJsBshift', 'Scope', 'PyExceptionToJs', - 'JsToPyException', 'JS_BUILTINS', 'appengine', 'set_global_object', - 'JsRegExp', 'PyJsException', 'PyExceptionToJs', 'JsToPyException', 'PyJsSwitchException'] - +__all__ = [ + 'Js', 'PyJsComma', 'PyJsStrictEq', 'PyJsStrictNeq', 'PyJsException', + 'PyJsBshift', 'Scope', 'PyExceptionToJs', 'JsToPyException', 'JS_BUILTINS', + 'appengine', 'set_global_object', 'JsRegExp', 'PyJsException', + 'PyExceptionToJs', 'JsToPyException', 'PyJsSwitchException' +] # these were defined in base.py -builtins = ('true','false','null','undefined','Infinity', - 'NaN', 'console', 'String', 'Number', 'Boolean', 'RegExp', - 'Math', 'Date', 'Object', 'Function', 'Array', - 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', - 'Int16Array','Uint16Array', - 'Int32Array', 'Uint32Array', - 'Float32Array', 'Float64Array', - 'ArrayBuffer', - 'parseFloat', 'parseInt', 'isFinite', 'isNaN', - 'escape', 'unescape', 'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent', - ) - #Array, Function, JSON, Error is done later :) - # also some built in functions like eval... +builtins = ( + 'true', + 'false', + 'null', + 'undefined', + 'Infinity', + 'NaN', + 'console', + 'String', + 'Number', + 'Boolean', + 'RegExp', + 'Math', + 'Date', + 'Object', + 'Function', + 'Array', + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'ArrayBuffer', + 'parseFloat', + 'parseInt', + 'isFinite', + 'isNaN', + 'escape', + 'unescape', + 'encodeURI', + 'decodeURI', + 'encodeURIComponent', + 'decodeURIComponent', +) + +#Array, Function, JSON, Error is done later :) +# also some built in functions like eval... + def set_global_object(obj): obj.IS_CHILD_SCOPE = False @@ -61,7 +90,6 @@ def set_global_object(obj): obj.put('window', this) - scope = dict(zip(builtins, [globals()[e] for e in builtins])) # Now add errors: for name, error in ERRORS.items(): @@ -69,5 +97,4 @@ def set_global_object(obj): #add eval scope['eval'] = Eval scope['JSON'] = JSON -JS_BUILTINS = dict((k,v) for k,v in scope.items()) - +JS_BUILTINS = dict((k, v) for k, v in scope.items()) diff --git a/js2py/translators/__init__.py b/js2py/translators/__init__.py index 48acaa1a..7a1001fd 100644 --- a/js2py/translators/__init__.py +++ b/js2py/translators/__init__.py @@ -18,8 +18,10 @@ # 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 -__all__ = ['PyJsParser', 'Node', 'WrappingNode', 'node_to_dict', 'parse', 'translate_js', 'translate', 'syntax_tree_translate', - 'DEFAULT_HEADER'] +__all__ = [ + 'PyJsParser', 'Node', 'WrappingNode', 'node_to_dict', 'parse', + 'translate_js', 'translate', 'syntax_tree_translate', 'DEFAULT_HEADER' +] __author__ = 'Piotr Dabkowski' __version__ = '2.2.0' from pyjsparser import PyJsParser @@ -34,5 +36,3 @@ def parse(javascript_code): Same as PyJsParser().parse For your convenience :) """ p = PyJsParser() return p.parse(javascript_code) - - diff --git a/js2py/translators/friendly_nodes.py b/js2py/translators/friendly_nodes.py index 835cc827..370f85d8 100644 --- a/js2py/translators/friendly_nodes.py +++ b/js2py/translators/friendly_nodes.py @@ -10,14 +10,19 @@ REGEXP_CONVERTER = PyJsParser() + def to_hex(s): - return binascii.hexlify(s.encode('utf8')).decode('utf8') # fucking python 3, I hate it so much - # wtf was wrong with s.encode('hex') ??? + return binascii.hexlify(s.encode('utf8')).decode( + 'utf8') # fucking python 3, I hate it so much + + + # wtf was wrong with s.encode('hex') ??? def indent(lines, ind=4): - return ind*' '+lines.replace('\n', '\n'+ind*' ').rstrip(' ') + return ind * ' ' + lines.replace('\n', '\n' + ind * ' ').rstrip(' ') + def inject_before_lval(source, lval, code): - if source.count(lval)>1: + if source.count(lval) > 1: print() print(lval) raise RuntimeError('To many lvals (%s)' % lval) @@ -29,39 +34,44 @@ def inject_before_lval(source, lval, code): end = source.index(lval) inj = source.rfind('\n', 0, end) ind = inj - while source[ind+1]==' ': - ind+=1 + while source[ind + 1] == ' ': + ind += 1 ind -= inj - return source[:inj+1]+ indent(code, ind) + source[inj+1:] + return source[:inj + 1] + indent(code, ind) + source[inj + 1:] def get_continue_label(label): - return CONTINUE_LABEL%to_hex(label) + return CONTINUE_LABEL % to_hex(label) + def get_break_label(label): - return BREAK_LABEL%to_hex(label) + return BREAK_LABEL % to_hex(label) def is_valid_py_name(name): try: - compile(name+' = 11', 'a','exec') + compile(name + ' = 11', 'a', 'exec') except: return False return True + def indent(lines, ind=4): - return ind*' '+lines.replace('\n', '\n'+ind*' ').rstrip(' ') + return ind * ' ' + lines.replace('\n', '\n' + ind * ' ').rstrip(' ') + def compose_regex(val): reg, flags = val #reg = REGEXP_CONVERTER._unescape_string(reg) return u'/%s/%s' % (reg, flags) + def float_repr(f): - if int(f)==f: + if int(f) == f: return repr(int(f)) return repr(f) + def argsplit(args, sep=','): """used to split JS args (it is not that simple as it seems because sep can be inside brackets). @@ -69,238 +79,275 @@ def argsplit(args, sep=','): pass args *without* brackets! Used also to parse array and object elements, and more""" - parsed_len = 0 + parsed_len = 0 last = 0 splits = [] for e in bracket_split(args, brackets=['()', '[]', '{}']): if e[0] not in ('(', '[', '{'): for i, char in enumerate(e): - if char==sep: - splits.append(args[last:parsed_len+i]) + if char == sep: + splits.append(args[last:parsed_len + i]) last = parsed_len + i + 1 parsed_len += len(e) splits.append(args[last:]) return splits -def bracket_split(source, brackets=('()','{}','[]'), strip=False): +def bracket_split(source, brackets=('()', '{}', '[]'), strip=False): """DOES NOT RETURN EMPTY STRINGS (can only return empty bracket content if strip=True)""" starts = [e[0] for e in brackets] in_bracket = 0 n = 0 last = 0 - while n='+b+')' + return '(' + a + '>=' + b + ')' + def js_gt(a, b): - return '('+a+'>'+b+')' + return '(' + a + '>' + b + ')' + def js_in(a, b): - return b+'.contains('+a+')' + return b + '.contains(' + a + ')' + def js_instanceof(a, b): - return a+'.instanceof('+b+')' + return a + '.instanceof(' + b + ')' + def js_lshift(a, b): - return '('+a+'<<'+b+')' + return '(' + a + '<<' + b + ')' + def js_rshift(a, b): - return '('+a+'>>'+b+')' + return '(' + a + '>>' + b + ')' + def js_shit(a, b): - return 'PyJsBshift('+a+','+b+')' + return 'PyJsBshift(' + a + ',' + b + ')' + + +def js_add( + a, + b): # To simplify later process of converting unary operators + and ++ + return '(%s+%s)' % (a, b) -def js_add(a, b): # To simplify later process of converting unary operators + and ++ - return '(%s+%s)'%(a, b) def js_sub(a, b): # To simplify - return '(%s-%s)'%(a, b) + return '(%s-%s)' % (a, b) + def js_mul(a, b): - return '('+a+'*'+b+')' + return '(' + a + '*' + b + ')' + def js_div(a, b): - return '('+a+'/'+b+')' + return '(' + a + '/' + b + ')' + def js_mod(a, b): - return '('+a+'%'+b+')' + return '(' + a + '%' + b + ')' + def js_typeof(a): - cand = list(bracket_split(a, ('()',))) - if len(cand)==2 and cand[0]=='var.get': - return cand[0]+cand[1][:-1]+',throw=False).typeof()' - return a+'.typeof()' + cand = list(bracket_split(a, ('()', ))) + if len(cand) == 2 and cand[0] == 'var.get': + return cand[0] + cand[1][:-1] + ',throw=False).typeof()' + return a + '.typeof()' + def js_void(a): # eval and return undefined return 'PyJsComma(%s, Js(None))' % a + def js_new(a): - cands = list(bracket_split(a, ('()',))) + cands = list(bracket_split(a, ('()', ))) lim = len(cands) if lim < 2: return a + '.create()' n = 0 while n < lim: c = cands[n] - if c[0]=='(': - if cands[n-1].endswith('.get') and n+1>=lim: # last get operation. + if c[0] == '(': + if cands[n - 1].endswith( + '.get') and n + 1 >= lim: # last get operation. return a + '.create()' - elif cands[n-1][0]=='(': - return ''.join(cands[:n])+'.create' + c + ''.join(cands[n+1:]) - elif cands[n-1]=='.callprop': - beg = ''.join(cands[:n-1]) - args = argsplit(c[1:-1],',') - prop = args[0] - new_args = ','.join(args[1:]) - create = '.get(%s).create(%s)' % (prop, new_args) - return beg + create + ''.join(cands[n+1:]) - n+=1 + elif cands[n - 1][0] == '(': + return ''.join(cands[:n]) + '.create' + c + ''.join( + cands[n + 1:]) + elif cands[n - 1] == '.callprop': + beg = ''.join(cands[:n - 1]) + args = argsplit(c[1:-1], ',') + prop = args[0] + new_args = ','.join(args[1:]) + create = '.get(%s).create(%s)' % (prop, new_args) + return beg + create + ''.join(cands[n + 1:]) + n += 1 return a + '.create()' def js_delete(a): #replace last get with delete. c = list(bracket_split(a, ['()'])) - beg, arglist = ''.join(c[:-1]).strip(), c[-1].strip() #strips just to make sure... I will remove it later - if beg[-4:]!='.get': + beg, arglist = ''.join(c[:-1]).strip(), c[-1].strip( + ) #strips just to make sure... I will remove it later + if beg[-4:] != '.get': print(a) raise SyntaxError('Invalid delete operation') - return beg[:-3]+'delete'+arglist + return beg[:-3] + 'delete' + arglist def js_neg(a): - return '(-'+a+')' + return '(-' + a + ')' + def js_pos(a): - return '(+'+a+')' + return '(+' + a + ')' + def js_inv(a): - return '(~'+a+')' + return '(~' + a + ')' + def js_not(a): - return a+'.neg()' + return a + '.neg()' + def js_postfix(a, inc, post): - bra = list(bracket_split(a, ('()',))) + bra = list(bracket_split(a, ('()', ))) meth = bra[-2] if not meth.endswith('get'): raise SyntaxError('Invalid ++ or -- operation.') bra[-2] = bra[-2][:-3] + 'put' - bra[-1] = '(%s,Js(%s.to_number())%sJs(1))' % (bra[-1][1:-1], a, '+' if inc else '-') + bra[-1] = '(%s,Js(%s.to_number())%sJs(1))' % (bra[-1][1:-1], a, + '+' if inc else '-') res = ''.join(bra) return res if not post else '(%s%sJs(1))' % (res, '-' if inc else '+') + def js_pre_inc(a): return js_postfix(a, True, False) + def js_post_inc(a): return js_postfix(a, True, True) + def js_pre_dec(a): return js_postfix(a, False, False) + def js_post_dec(a): return js_postfix(a, False, True) - CONTINUE_LABEL = 'JS_CONTINUE_LABEL_%s' BREAK_LABEL = 'JS_BREAK_LABEL_%s' PREPARE = '''HOLDER = var.own.get(NAME)\nvar.force_own_put(NAME, PyExceptionToJs(PyJsTempException))\n''' RESTORE = '''if HOLDER is not None:\n var.own[NAME] = HOLDER\nelse:\n del var.own[NAME]\ndel HOLDER\n''' TRY_CATCH = '''%stry:\nBLOCKfinally:\n%s''' % (PREPARE, indent(RESTORE)) - - OR = {'||': js_or} AND = {'&&': js_and} BOR = {'|': js_bor} BXOR = {'^': js_bxor} BAND = {'&': js_band} -EQS = {'===': js_strict_eq, - '!==': js_strict_neq, - '==': js_abstract_eq, # we need == and != too. Read a note above method - '!=': js_abstract_neq} +EQS = { + '===': js_strict_eq, + '!==': js_strict_neq, + '==': js_abstract_eq, # we need == and != too. Read a note above method + '!=': js_abstract_neq +} #Since JS does not have chained comparisons we need to implement all cmp methods. -COMPS = {'<': js_lt, - '<=': js_le, - '>=': js_ge, - '>': js_gt, - 'instanceof': js_instanceof, #todo change to validitate - 'in': js_in} - -BSHIFTS = {'<<': js_lshift, - '>>': js_rshift, - '>>>': js_shit} - -ADDS = {'+': js_add, - '-': js_sub} - -MULTS = {'*': js_mul, - '/': js_div, - '%': js_mod} +COMPS = { + '<': js_lt, + '<=': js_le, + '>=': js_ge, + '>': js_gt, + 'instanceof': js_instanceof, #todo change to validitate + 'in': js_in +} + +BSHIFTS = {'<<': js_lshift, '>>': js_rshift, '>>>': js_shit} + +ADDS = {'+': js_add, '-': js_sub} + +MULTS = {'*': js_mul, '/': js_div, '%': js_mod} BINARY = {} BINARY.update(ADDS) BINARY.update(MULTS) @@ -314,14 +361,15 @@ def js_post_dec(a): BINARY.update(OR) #Note they dont contain ++ and -- methods because they both have 2 different methods # correct method will be found automatically in translate function -UNARY = {'typeof': js_typeof, - 'void': js_void, - 'new': js_new, - 'delete': js_delete, - '!': js_not, - '-': js_neg, - '+': js_pos, - '~': js_inv, - '++': None, - '--': None - } \ No newline at end of file +UNARY = { + 'typeof': js_typeof, + 'void': js_void, + 'new': js_new, + 'delete': js_delete, + '!': js_not, + '-': js_neg, + '+': js_pos, + '~': js_inv, + '++': None, + '--': None +} diff --git a/js2py/translators/jsregexps.py b/js2py/translators/jsregexps.py index 7a976cc3..235d67c7 100644 --- a/js2py/translators/jsregexps.py +++ b/js2py/translators/jsregexps.py @@ -2,22 +2,28 @@ REGEXP_SPECIAL_SINGLE = {'\\', '^', '$', '*', '+', '?', '.'} -NOT_PATTERN_CHARS = {'^', '$', '\\', '.', '*', '+', '?', '(', ')', '[', ']', '|'} # what about '{', '}', ??? +NOT_PATTERN_CHARS = { + '^', '$', '\\', '.', '*', '+', '?', '(', ')', '[', ']', '|' +} # what about '{', '}', ??? CHAR_CLASS_ESCAPE = {'d', 'D', 's', 'S', 'w', 'W'} CONTROL_ESCAPE_CHARS = {'f', 'n', 'r', 't', 'v'} -CONTROL_LETTERS = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'} +CONTROL_LETTERS = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' +} + def SpecialChar(char): - return {'type': 'SpecialChar', - 'content': char} + return {'type': 'SpecialChar', 'content': char} def isPatternCharacter(char): return char not in NOT_PATTERN_CHARS + class JsRegExpParser: def __init__(self, source, flags): self.source = source @@ -27,11 +33,9 @@ def __init__(self, source, flags): self.lineNumber = 0 self.lineStart = 0 - def parsePattern(self): '''Perform sctring escape - for regexp literals''' - return {'type': 'Pattern', - 'contents': self.parseDisjunction()} + return {'type': 'Pattern', 'contents': self.parseDisjunction()} def parseDisjunction(self): alternatives = [] @@ -41,29 +45,28 @@ def parseDisjunction(self): self.expect_character('|') else: break - return {'type': 'Disjunction', - 'contents': alternatives} + return {'type': 'Disjunction', 'contents': alternatives} def isEOF(self): - if self.index>=self.length: + if self.index >= self.length: return True return False def expect_character(self, character): - if self.source[self.index]!=character: + if self.source[self.index] != character: self.throwUnexpected(character) self.index += 1 def parseAlternative(self): contents = [] - while not self.isEOF() and self.source[self.index]!='|': + while not self.isEOF() and self.source[self.index] != '|': contents.append(self.parseTerm()) - return {'type': 'Alternative', - 'contents': contents} + return {'type': 'Alternative', 'contents': contents} def follows(self, chars): for i, c in enumerate(chars): - if self.index+i>=self.length or self.source[self.index+i] != c: + if self.index + i >= self.length or self.source[self.index + + i] != c: return False return True @@ -72,9 +75,10 @@ def parseTerm(self): if assertion: return assertion else: - return {'type': 'Term', - 'contents': self.parseAtom()} # quantifier will go inside atom! - + return { + 'type': 'Term', + 'contents': self.parseAtom() + } # quantifier will go inside atom! def parseAssertion(self): if self.follows('$'): @@ -93,24 +97,19 @@ def parseAssertion(self): self.index += 3 dis = self.parseDisjunction() self.expect_character(')') - content = {'type': 'Lookached', - 'contents': dis, - 'negated': False} + content = {'type': 'Lookached', 'contents': dis, 'negated': False} elif self.follows('(?!'): self.index += 3 dis = self.parseDisjunction() self.expect_character(')') - content = {'type': 'Lookached', - 'contents': dis, - 'negated': True} + content = {'type': 'Lookached', 'contents': dis, 'negated': True} else: return None - return {'type': 'Assertion', - 'content': content} + return {'type': 'Assertion', 'content': content} def parseAtom(self): if self.follows('.'): - content = SpecialChar('.') + content = SpecialChar('.') self.index += 1 elif self.follows('\\'): self.index += 1 @@ -133,9 +132,7 @@ def parseAtom(self): else: return None quantifier = self.parseQuantifier() - return {'type': 'Atom', - 'content': content, - 'quantifier': quantifier} + return {'type': 'Atom', 'content': content, 'quantifier': quantifier} def parseQuantifier(self): prefix = self.parseQuantifierPrefix() @@ -145,9 +142,7 @@ def parseQuantifier(self): if self.follows('?'): self.index += 1 greedy = False - return {'type': 'Quantifier', - 'contents': prefix, - 'greedy': greedy} + return {'type': 'Quantifier', 'contents': prefix, 'greedy': greedy} def parseQuantifierPrefix(self): if self.isEOF(): @@ -161,7 +156,9 @@ def parseQuantifierPrefix(self): elif self.follows('*'): content = '*' self.index += 1 - elif self.follows('{'): # try matching otherwise return None and restore the state + elif self.follows( + '{' + ): # try matching otherwise return None and restore the state i = self.index self.index += 1 digs1 = self.scanDecimalDigs() @@ -186,10 +183,9 @@ def parseQuantifierPrefix(self): return None return content - def parseAtomEscape(self): ch = self.source[self.index] - if isDecimalDigit(ch) and ch!=0: + if isDecimalDigit(ch) and ch != 0: digs = self.scanDecimalDigs() elif ch in CHAR_CLASS_ESCAPE: self.index += 1 @@ -201,10 +197,9 @@ def parseCharacterEscape(self): ch = self.source[self.index] if ch in CONTROL_ESCAPE_CHARS: return SpecialChar('\\' + ch) - if ch=='c': + if ch == 'c': 'ok, fuck this shit.' - def scanDecimalDigs(self): s = self.index while not self.isEOF() and isDecimalDigit(self.source[self.index]): @@ -212,8 +207,5 @@ def scanDecimalDigs(self): return self.source[s:self.index] - - - a = JsRegExpParser('a(?=x)', '') -print(a.parsePattern()) \ No newline at end of file +print(a.parsePattern()) diff --git a/js2py/translators/translating_nodes.py b/js2py/translators/translating_nodes.py index e63539a5..0ae93dd9 100644 --- a/js2py/translators/translating_nodes.py +++ b/js2py/translators/translating_nodes.py @@ -11,7 +11,8 @@ # 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 :) +LINE_LEN_LIMIT = 400 # 200 # or any other value - the larger the smaller probability of errors :) + class ForController: def __init__(self): @@ -35,15 +36,15 @@ 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 + 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 @@ -77,26 +78,24 @@ def define(self, name, code): self.register(name) def get_code(self): - code = 'var.registers([%s])\n' % ', '.join(repr(e) for e in self.to_register) + 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': + if literal_or_identifier['type'] == 'Identifier': return literal_or_identifier['name'] - elif literal_or_identifier['type']=='Literal': + elif literal_or_identifier['type'] == 'Literal': k = literal_or_identifier['value'] if isinstance(k, float): return unicode(float_repr(k)) @@ -109,6 +108,7 @@ def to_key(literal_or_identifier): else: return unicode(k) + def trans(ele, standard=False): """Translates esprima syntax tree to python by delegating to appropriate translating node""" try: @@ -116,7 +116,8 @@ def trans(ele, standard=False): if not node: raise NotImplementedError('%s is not supported!' % ele['type']) if standard: - node = node.__dict__['standard'] if 'standard' in node.__dict__ else node + node = node.__dict__[ + 'standard'] if 'standard' in node.__dict__ else node return node(**ele) except: #print ele @@ -127,46 +128,47 @@ 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 + 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: + 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+'()' + 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 + 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"))' + 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) @@ -174,9 +176,9 @@ def MemberExpression(type, computed, object, property): # 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 + else: # worst case prop = trans(property) - else: # always the same since not computed (obj.prop accessor) + else: # always the same since not computed (obj.prop accessor) prop = repr(to_key(property)) return far_left + '.get(%s)' % prop @@ -184,26 +186,27 @@ def MemberExpression(type, computed, object, property): 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': + 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) + 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 + else: # standard call return trans(callee) + '(%s)' % ', '.join(arguments) - # ========== ARRAYS ============ @@ -211,33 +214,37 @@ 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 = inline_stack.require('Object') elems = [] after = '' for p in properties: - if p['kind']=='init': + if p['kind'] == 'init': elems.append('%s:%s' % Property(**p)) - elif 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': + elif 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) + after += '%s.define_own_property(%s, {"get":%s, "configurable":True, "enumerable":True})\n' % ( + name, k, getter) else: raise RuntimeError('Unexpected object propery kind') obj = '%s = Js({%s})\n' % (name, ','.join(elems)) - inline_stack.define(name, obj+after) + inline_stack.define(name, obj + after) return name - def Property(type, kind, key, computed, value, method, shorthand): if shorthand or computed: - raise NotImplementedError('Shorthand and Computed properties not implemented!') + 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') @@ -250,72 +257,92 @@ def Property(type, kind, key, computed, value, method, shorthand): @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': + 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 '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) + 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) + 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 left['type'] == 'Identifier': if operator: - return 'var.put(%s, %s, %s)' % (repr(to_key(left)), trans(right), repr(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': + 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) + 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)) + 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) + 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)) +@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 + return StatementList( + body) # never returns empty string! In the worst case returns pass\n def ExpressionStatement(type, expression): @@ -335,8 +362,10 @@ def ContinueStatement(type, label): else: return 'continue\n' + def ReturnStatement(type, argument): - return 'return %s\n' % (trans(argument) if argument else "var.get('undefined')") + return 'return %s\n' % (trans(argument) + if argument else "var.get('undefined')") def EmptyStatement(type): @@ -353,15 +382,15 @@ def DoWhileStatement(type, body, test): return result - def ForStatement(type, init, test, update, body): update = indent(trans(update)) if update else '' - init = trans(init) if init 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) + 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) @@ -370,17 +399,17 @@ def ForStatement(type, init, test, update, body): 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 + 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 :( + 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': + elif left['type'] == 'Identifier': name = left['name'] else: raise RuntimeError('Unusual ForIn loop') @@ -402,19 +431,20 @@ 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'): + 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 + 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 + inside = 'try:\n%sexcept %s:\n pass\n' % (indent(inside), break_label) + defs += 'class %s(Exception): pass\n' % break_label return defs + inside @@ -425,6 +455,7 @@ def StatementList(lis): else: return 'pass\n' + def PyimportStatement(type, imp): lib = imp['name'] jlib = 'PyImport_%s' % lib @@ -433,19 +464,22 @@ def PyimportStatement(type, imp): try: compile(code, '', 'exec') except: - raise SyntaxError('Invalid Python module name (%s) in pyimport statement'%lib) + 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'])) + 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') @@ -458,7 +492,8 @@ def SwitchStatement(type, discriminant, cases): def ThrowStatement(type, argument): - return 'PyJsTempException = JsToPyException(%s)\nraise PyJsTempException\n' % trans(argument) + return 'PyJsTempException = JsToPyException(%s)\nraise PyJsTempException\n' % trans( + argument) def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer): @@ -466,11 +501,15 @@ def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer): # complicated catch statement... if handler: identifier = handler['param']['name'] - holder = 'PyJsHolder_%s_%d'%(to_hex(identifier), random.randrange(1e8)) + 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'])))) + 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)) @@ -478,7 +517,9 @@ def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer): def LexicalDeclaration(type, declarations, kind): - raise NotImplementedError('let and const not implemented yet but they will be soon! Check github for updates.') + raise NotImplementedError( + 'let and const not implemented yet but they will be soon! Check github for updates.' + ) def VariableDeclarator(type, id, init): @@ -496,7 +537,7 @@ def VariableDeclaration(type, declarations, kind): def WhileStatement(type, test, body): - result = 'while %s:\n'%trans(test) + indent(trans(body)) + result = 'while %s:\n' % trans(test) + indent(trans(body)) return result @@ -514,16 +555,18 @@ def Program(type, body): return code - # ======== FUNCTIONS ============ -def FunctionDeclaration(type, id, params, defaults, body, generator, expression): + +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' + 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' @@ -545,18 +588,20 @@ def FunctionDeclaration(type, id, params, defaults, body, generator, expression) 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 + 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 '')) + 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)) + 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 + 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 @@ -564,7 +609,8 @@ def FunctionDeclaration(type, id, params, defaults, body, generator, expression) return 'pass\n' -def FunctionExpression(type, id, params, defaults, body, generator, expression): +def FunctionExpression(type, id, params, defaults, body, generator, + expression): if generator: raise NotImplementedError('Generators not supported') if defaults: @@ -594,20 +640,22 @@ def FunctionExpression(type, id, params, defaults, body, generator, expression): 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 + 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 '')) + 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... + 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)) + 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 + whole_code = header + indent(arg_conv + code) + footer # restore context Context = previous_context # define in upper context @@ -620,12 +668,12 @@ def FunctionExpression(type, id, params, defaults, body, generator, expression): clean_stacks() -if __name__=='__main__': +if __name__ == '__main__': import codecs import time import pyjsparser - c = None#'''`ijfdij`''' + c = None #'''`ijfdij`''' if not c: with codecs.open("esp.js", "r", "utf-8") as f: c = f.read() @@ -633,9 +681,8 @@ def FunctionExpression(type, id, params, defaults, body, generator, expression): 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)) + 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) - diff --git a/js2py/translators/translator.py b/js2py/translators/translator.py index 9e10b2aa..66bf9abe 100644 --- a/js2py/translators/translator.py +++ b/js2py/translators/translator.py @@ -5,7 +5,6 @@ import hashlib import re - # Enable Js2Py exceptions and pyimport in parser pyjsparser.parser.ENABLE_PYIMPORT = True @@ -16,8 +15,7 @@ CP_NUMERIC_RE = re.compile(r'(?> 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)] + 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 :) @@ -213,27 +204,32 @@ def check(code_obj): 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]) + 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') + raise RuntimeError( + 'Your python version made changes to the bytecode') check(six.get_function_code(check)) - - -if __name__=='__main__': +if __name__ == '__main__': x = 'Wrong' dick = 3000 + def func(a): - print(x,y,z, a) + print(x, y, z, a) print(dick) - d = (x,) - for e in (e for e in x): + 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 + + 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 diff --git a/setup.cfg b/setup.cfg index 224a7795..5707cb06 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,5 @@ [metadata] -description-file = README.md \ No newline at end of file +description-file = README.md + +[yapf] +based_on_style = pep8 \ No newline at end of file diff --git a/simple_test.py b/simple_test.py index 3c3078c9..fe4701e7 100644 --- a/simple_test.py +++ b/simple_test.py @@ -97,13 +97,13 @@ def get_bar(self): } ''' # we can even parse esprima's own source code, but its size is too big for the travis test. -# sample_js_code = js2py.get_file_contents('examples/esprima.js') +#sample_js_code = js2py.get_file_contents('examples/esprima.js') parsed = esprima.parse(sample_js_code) print('use escodegen to get back the js code from the parsed AST') reconstructed = escodegen.generate(parsed) parsed2 = esprima.parse(sample_js_code) reconstructed2 = escodegen.generate(parsed) -assert reconstructed==reconstructed2 and len(reconstructed)>=len(sample_js_code) and reconstructed.startswith('function') +assert reconstructed==reconstructed2 and len(reconstructed)>=len(sample_js_code) # chalk ( https://github.com/chalk/chalk ) From cfb315a321a5abaeca3091a56d21fd43ee59c058 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sun, 18 Nov 2018 00:40:03 +0100 Subject: [PATCH 30/77] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e797567..cd7a9997 100644 --- a/README.md +++ b/README.md @@ -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 From e674a9507df03810cc072db66b2a00eaa92f30c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Jungbeck?= Date: Wed, 21 Nov 2018 18:14:20 +0100 Subject: [PATCH 31/77] Make compatible with Python3 execfile does not exist in Python 3 --- js2py/evaljs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js2py/evaljs.py b/js2py/evaljs.py index b67faf61..ea1b70aa 100644 --- a/js2py/evaljs.py +++ b/js2py/evaljs.py @@ -200,7 +200,10 @@ def execute_debug(self, js): try: with open(filename, mode='w') as f: f.write(code) - execfile(filename, self._context) + with open(filename, "r") as f: + pyCode = compile(f.read(), filename, 'exec') + exec(pyCode, self._context) + except Exception as err: raise err finally: From 87cb410c2c11de19e3884c45e5e5b6b807d933c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Jungbeck?= Date: Wed, 21 Nov 2018 21:52:33 +0100 Subject: [PATCH 32/77] Make compatible with Python 3 code has type str. --- js2py/evaljs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js2py/evaljs.py b/js2py/evaljs.py index ea1b70aa..3f5eeee5 100644 --- a/js2py/evaljs.py +++ b/js2py/evaljs.py @@ -196,7 +196,7 @@ def execute_debug(self, js): code = translate_js(js, '') # make sure you have a temp folder: filename = 'temp' + os.sep + '_' + hashlib.md5( - code).hexdigest() + '.py' + code.encode("utf-8")).hexdigest() + '.py' try: with open(filename, mode='w') as f: f.write(code) From 0610132fb0a223df85bc0bc3180db6e0d3c74378 Mon Sep 17 00:00:00 2001 From: McArcady Date: Sun, 30 Dec 2018 16:14:47 +0100 Subject: [PATCH 33/77] console: add methods debug,info,warn,error --- js2py/host/console.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js2py/host/console.py b/js2py/host/console.py index 3b44a479..50a08632 100644 --- a/js2py/host/console.py +++ b/js2py/host/console.py @@ -1,14 +1,15 @@ from ..base import * - @Js def console(): pass - @Js def log(): print(arguments[0]) - console.put('log', log) +console.put('debug', log) +console.put('info', log) +console.put('warn', log) +console.put('error', log) From 4ddc2482646b7368ce91627a8d030cdd25c786f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Wed, 2 Jan 2019 16:06:11 +0100 Subject: [PATCH 34/77] Fix several DeprecationWarning: invalid escape sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mickaël Schoentgen --- js2py/translators/translator.py | 4 ++-- tests/run.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js2py/translators/translator.py b/js2py/translators/translator.py index 66bf9abe..16ed5bdb 100644 --- a/js2py/translators/translator.py +++ b/js2py/translators/translator.py @@ -15,7 +15,7 @@ CP_NUMERIC_RE = re.compile(r'(? Date: Thu, 3 Jan 2019 19:22:45 +0000 Subject: [PATCH 35/77] Convert js objects to imperfect python primitives implicitly (if requested). --- js2py/base.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/js2py/base.py b/js2py/base.py index 6043ed0e..67c80d59 100644 --- a/js2py/base.py +++ b/js2py/base.py @@ -50,7 +50,12 @@ def to_python(val): return val.value elif isinstance(val, PyObjectWrapper): return val.__dict__['obj'] - return JsObjectWrapper(val) + elif isinstance(val, PyJsArray) and val.CONVERT_TO_PY_PRIMITIVES: + return to_list(val) + elif isinstance(val, PyJsObject) and val.CONVERT_TO_PY_PRIMITIVES: + return to_dict(val) + else: + return JsObjectWrapper(val) def to_dict(js_obj, @@ -255,6 +260,7 @@ class PyJs(object): own = {} GlobalObject = None IS_CHILD_SCOPE = False + CONVERT_TO_PY_PRIMITIVES = False value = None buff = None From 7a3a1ffc6c153e4ea867988d12725f92d133ffc4 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sat, 9 Mar 2019 21:58:55 +0100 Subject: [PATCH 36/77] Add a function returning js bytecode. --- js2py/internals/seval.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/js2py/internals/seval.py b/js2py/internals/seval.py index a3ed39c0..c4404ab7 100644 --- a/js2py/internals/seval.py +++ b/js2py/internals/seval.py @@ -10,7 +10,12 @@ pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError(u'SyntaxError', unicode(msg)) - +def get_js_bytecode(js): + a = ByteCodeGenerator(Code()) + d = pyjsparser.parse(js) + a.emit(d) + return a.exe.tape + def eval_js_vm(js): a = ByteCodeGenerator(Code()) s = Space() From 8057725ec0e3eb3bd2004208f30e3e2fc1909645 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sat, 13 Apr 2019 17:37:51 +0200 Subject: [PATCH 37/77] vm improvements --- js2py/evaljs.py | 2 + js2py/internals/base.py | 18 +++-- js2py/internals/byte_trans.py | 23 +++---- js2py/internals/code.py | 66 ++++++++++++++----- js2py/internals/constructors/jsfunction.py | 3 +- js2py/internals/conversions.py | 2 +- js2py/internals/fill_space.py | 45 ++++++------- js2py/internals/func_utils.py | 4 +- js2py/internals/opcodes.py | 4 +- js2py/internals/operations.py | 4 +- js2py/internals/prototypes/jsstring.py | 2 +- js2py/internals/seval.py | 21 +++--- js2py/internals/simplex.py | 8 ++- js2py/internals/space.py | 4 +- js2py/internals/trans_utils.py | 7 ++ setup.py | 4 +- tests/run.py | 12 ++-- .../statements/function/S13.2.1_A1_T1.js | 63 ------------------ 18 files changed, 138 insertions(+), 154 deletions(-) diff --git a/js2py/evaljs.py b/js2py/evaljs.py index 3f5eeee5..64eea5c4 100644 --- a/js2py/evaljs.py +++ b/js2py/evaljs.py @@ -116,10 +116,12 @@ def eval_js(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)) diff --git a/js2py/internals/base.py b/js2py/internals/base.py index ec277c64..a02a2122 100644 --- a/js2py/internals/base.py +++ b/js2py/internals/base.py @@ -3,15 +3,19 @@ import datetime -from desc import * -from simplex import * -from conversions import * -import six +from .desc import * +from .simplex import * +from .conversions import * + from pyjsparser import PyJsParser -from itertools import izip -from conversions import * -from simplex import * +import six +if six.PY2: + from itertools import izip +else: + izip = zip + + def Type(obj): diff --git a/js2py/internals/byte_trans.py b/js2py/internals/byte_trans.py index 87fab4b4..e32bcb1e 100644 --- a/js2py/internals/byte_trans.py +++ b/js2py/internals/byte_trans.py @@ -1,8 +1,8 @@ -from code import Code -from simplex import MakeError -from opcodes import * -from operations import * -from trans_utils import * +from .code import Code +from .simplex import MakeError +from .opcodes import * +from .operations import * +from .trans_utils import * SPECIAL_IDENTIFIERS = {'true', 'false', 'this'} @@ -465,10 +465,11 @@ def ObjectExpression(self, properties, **kwargs): self.emit('LOAD_OBJECT', tuple(data)) def Program(self, body, **kwargs): + old_tape_len = len(self.exe.tape) self.emit('LOAD_UNDEFINED') self.emit(body) # add function tape ! - self.exe.tape = self.function_declaration_tape + self.exe.tape + self.exe.tape = self.exe.tape[:old_tape_len] + self.function_declaration_tape + self.exe.tape[old_tape_len:] def Pyimport(self, imp, **kwargs): raise NotImplementedError( @@ -735,17 +736,17 @@ def main(): # # } a.emit(d) - print a.declared_vars - print a.exe.tape - print len(a.exe.tape) + print(a.declared_vars) + print(a.exe.tape) + print(len(a.exe.tape)) a.exe.compile() def log(this, args): - print args[0] + print(args[0]) return 999 - print a.exe.run(a.exe.space.GlobalObj) + print(a.exe.run(a.exe.space.GlobalObj)) if __name__ == '__main__': diff --git a/js2py/internals/code.py b/js2py/internals/code.py index 6bd6739f..9af0e602 100644 --- a/js2py/internals/code.py +++ b/js2py/internals/code.py @@ -1,16 +1,17 @@ -from opcodes import * -from space import * -from base import * +from .opcodes import * +from .space import * +from .base import * class Code: '''Can generate, store and run sequence of ops representing js code''' - def __init__(self, is_strict=False): + def __init__(self, is_strict=False, debug_mode=False): self.tape = [] self.compiled = False self.label_locs = None self.is_strict = is_strict + self.debug_mode = debug_mode self.contexts = [] self.current_ctx = None @@ -22,6 +23,10 @@ def __init__(self, is_strict=False): self.GLOBAL_THIS = None self.space = None + # dbg + self.ctx_depth = 0 + + def get_new_label(self): self._label_count += 1 return self._label_count @@ -74,21 +79,35 @@ def execute_fragment_under_context(self, ctx, start_label, end_label): # 0=normal, 1=return, 2=jump_outside, 3=errors # execute_fragment_under_context returns: # (return_value, typ, return_value/jump_loc/py_error) - # ctx.stack must be len 1 and its always empty after the call. + # IMPARTANT: It is guaranteed that the length of the ctx.stack is unchanged. ''' old_curr_ctx = self.current_ctx + self.ctx_depth += 1 + old_stack_len = len(ctx.stack) + old_ret_len = len(self.return_locs) + old_ctx_len = len(self.contexts) try: self.current_ctx = ctx return self._execute_fragment_under_context( ctx, start_label, end_label) except JsException as err: - # undo the things that were put on the stack (if any) - # don't worry, I know the recovery is possible through try statement and for this reason try statement - # has its own context and stack so it will not delete the contents of the outer stack - del ctx.stack[:] + if self.debug_mode: + self._on_fragment_exit("js errors") + # undo the things that were put on the stack (if any) to ensure a proper error recovery + del ctx.stack[old_stack_len:] + del self.return_locs[old_ret_len:] + del self.contexts[old_ctx_len :] return undefined, 3, err finally: + self.ctx_depth -= 1 self.current_ctx = old_curr_ctx + assert old_stack_len == len(ctx.stack) + + def _get_dbg_indent(self): + return self.ctx_depth * ' ' + + def _on_fragment_exit(self, mode): + print(self._get_dbg_indent() + 'ctx exit (%s)' % mode) def _execute_fragment_under_context(self, ctx, start_label, end_label): start, end = self.label_locs[start_label], self.label_locs[end_label] @@ -97,16 +116,20 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): entry_level = len(self.contexts) # for e in self.tape[start:end]: # print e - + if self.debug_mode: + print(self._get_dbg_indent() + 'ctx entry (from:%d, to:%d)' % (start, end)) while loc < len(self.tape): - #print loc, self.tape[loc] if len(self.contexts) == entry_level and loc >= end: + if self.debug_mode: + self._on_fragment_exit('normal') assert loc == end - assert len(ctx.stack) == ( - 1 + initial_len), 'Stack change must be equal to +1!' + delta_stack = len(ctx.stack) - initial_len + assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack return ctx.stack.pop(), 0, None # means normal return # execute instruction + if self.debug_mode: + print(self._get_dbg_indent() + str(loc), self.tape[loc]) status = self.tape[loc].eval(ctx) # check status for special actions @@ -116,9 +139,10 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): if len(self.contexts) == entry_level: # check if jumped outside of the fragment and break if so if not start <= loc < end: - assert len(ctx.stack) == ( - 1 + initial_len - ), 'Stack change must be equal to +1!' + if self.debug_mode: + self._on_fragment_exit('jump outside loc:%d label:%d' % (loc, status)) + delta_stack = len(ctx.stack) - initial_len + assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack return ctx.stack.pop(), 2, status # jump outside continue @@ -137,7 +161,10 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): # return: (None, None) else: if len(self.contexts) == entry_level: - assert len(ctx.stack) == 1 + initial_len + if self.debug_mode: + self._on_fragment_exit('return') + delta_stack = len(ctx.stack) - initial_len + assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack return undefined, 1, ctx.stack.pop( ) # return signal return_value = ctx.stack.pop() @@ -149,6 +176,8 @@ def _execute_fragment_under_context(self, ctx, start_label, end_label): continue # next instruction loc += 1 + if self.debug_mode: + self._on_fragment_exit('internal error - unexpected end of tape, will crash') assert False, 'Remember to add NOP at the end!' def run(self, ctx, starting_loc=0): @@ -156,7 +185,8 @@ def run(self, ctx, starting_loc=0): self.current_ctx = ctx while loc < len(self.tape): # execute instruction - #print loc, self.tape[loc] + if self.debug_mode: + print(loc, self.tape[loc]) status = self.tape[loc].eval(ctx) # check status for special actions diff --git a/js2py/internals/constructors/jsfunction.py b/js2py/internals/constructors/jsfunction.py index 9728fb38..d62731ac 100644 --- a/js2py/internals/constructors/jsfunction.py +++ b/js2py/internals/constructors/jsfunction.py @@ -42,6 +42,7 @@ def executable_code(code_str, space, global_context=True): 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! @@ -71,5 +72,5 @@ def _eval(this, args): def log(this, args): - print ' '.join(map(to_string, args)) + print(' '.join(map(to_string, args))) return undefined diff --git a/js2py/internals/conversions.py b/js2py/internals/conversions.py index b90a427d..4b107215 100644 --- a/js2py/internals/conversions.py +++ b/js2py/internals/conversions.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals # Type Conversions. to_type. All must return PyJs subclass instance -from simplex import * +from .simplex import * def to_primitive(self, hint=None): diff --git a/js2py/internals/fill_space.py b/js2py/internals/fill_space.py index 9aa9c4d2..329c8b28 100644 --- a/js2py/internals/fill_space.py +++ b/js2py/internals/fill_space.py @@ -1,29 +1,22 @@ from __future__ import unicode_literals -from base import Scope -from func_utils import * -from conversions import * +from .base import Scope +from .func_utils import * +from .conversions import * import six -from prototypes.jsboolean import BooleanPrototype -from prototypes.jserror import ErrorPrototype -from prototypes.jsfunction import FunctionPrototype -from prototypes.jsnumber import NumberPrototype -from prototypes.jsobject import ObjectPrototype -from prototypes.jsregexp import RegExpPrototype -from prototypes.jsstring import StringPrototype -from prototypes.jsarray import ArrayPrototype -import prototypes.jsjson as jsjson -import prototypes.jsutils as jsutils - -from constructors import jsnumber -from constructors import jsstring -from constructors import jsarray -from constructors import jsboolean -from constructors import jsregexp -from constructors import jsmath -from constructors import jsobject -from constructors import jsfunction -from constructors import jsconsole +from .prototypes.jsboolean import BooleanPrototype +from .prototypes.jserror import ErrorPrototype +from .prototypes.jsfunction import FunctionPrototype +from .prototypes.jsnumber import NumberPrototype +from .prototypes.jsobject import ObjectPrototype +from .prototypes.jsregexp import RegExpPrototype +from .prototypes.jsstring import StringPrototype +from .prototypes.jsarray import ArrayPrototype +from .prototypes import jsjson +from .prototypes import jsutils + +from .constructors import jsnumber, jsstring, jsarray, jsboolean, jsregexp, jsmath, jsobject, jsfunction, jsconsole + def fill_proto(proto, proto_class, space): @@ -155,7 +148,10 @@ def creator(this, args): j = easy_func(creator, space) j.name = unicode(typ) - j.prototype = space.ERROR_TYPES[typ] + + set_protected(j, 'prototype', space.ERROR_TYPES[typ]) + + set_non_enumerable(space.ERROR_TYPES[typ], 'constructor', j) def new_create(args, space): message = get_arg(args, 0) @@ -178,6 +174,7 @@ def new_create(args, space): setattr(space, err_type_name + u'Prototype', extra_err) error_constructors[err_type_name] = construct_constructor( err_type_name) + assert space.TypeErrorPrototype is not None # RegExp diff --git a/js2py/internals/func_utils.py b/js2py/internals/func_utils.py index 3c0b8d57..58dfef9e 100644 --- a/js2py/internals/func_utils.py +++ b/js2py/internals/func_utils.py @@ -1,5 +1,5 @@ -from simplex import * -from conversions import * +from .simplex import * +from .conversions import * import six if six.PY3: diff --git a/js2py/internals/opcodes.py b/js2py/internals/opcodes.py index 0f3127db..15c57ccd 100644 --- a/js2py/internals/opcodes.py +++ b/js2py/internals/opcodes.py @@ -1,5 +1,5 @@ -from operations import * -from base import get_member, get_member_dot, PyJsFunction, Scope +from .operations import * +from .base import get_member, get_member_dot, PyJsFunction, Scope class OP_CODE(object): diff --git a/js2py/internals/operations.py b/js2py/internals/operations.py index d9875088..35b90179 100644 --- a/js2py/internals/operations.py +++ b/js2py/internals/operations.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from simplex import * -from conversions import * +from .simplex import * +from .conversions import * # ------------------------------------------------------------------------------ # Unary operations diff --git a/js2py/internals/prototypes/jsstring.py b/js2py/internals/prototypes/jsstring.py index b56246e2..be38802e 100644 --- a/js2py/internals/prototypes/jsstring.py +++ b/js2py/internals/prototypes/jsstring.py @@ -4,7 +4,7 @@ import re from ..conversions import * from ..func_utils import * -from jsregexp import RegExpExec +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" diff --git a/js2py/internals/seval.py b/js2py/internals/seval.py index c4404ab7..147c9baf 100644 --- a/js2py/internals/seval.py +++ b/js2py/internals/seval.py @@ -1,11 +1,9 @@ import pyjsparser -from space import Space -import fill_space -from byte_trans import ByteCodeGenerator -from code import Code -from simplex import MakeError -import sys -sys.setrecursionlimit(100000) +from .space import Space +from . import fill_space +from .byte_trans import ByteCodeGenerator +from .code import Code +from .simplex import * pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError(u'SyntaxError', unicode(msg)) @@ -16,8 +14,8 @@ def get_js_bytecode(js): a.emit(d) return a.exe.tape -def eval_js_vm(js): - a = ByteCodeGenerator(Code()) +def eval_js_vm(js, debug_mode=False): + a = ByteCodeGenerator(Code(debug_mode=debug_mode)) s = Space() a.exe.space = s s.exe = a.exe @@ -26,7 +24,10 @@ def eval_js_vm(js): a.emit(d) fill_space.fill_space(s, a) - # print a.exe.tape + if debug_mode: + from pprint import pprint + pprint(a.exe.tape) + print() a.exe.compile() return a.exe.run(a.exe.space.GlobalObj) diff --git a/js2py/internals/simplex.py b/js2py/internals/simplex.py index b05f6174..0b07b2cc 100644 --- a/js2py/internals/simplex.py +++ b/js2py/internals/simplex.py @@ -1,6 +1,10 @@ from __future__ import unicode_literals import six - +if six.PY3: + basestring = str + long = int + xrange = range + unicode = str #Undefined class PyJsUndefined(object): @@ -114,7 +118,7 @@ def __str__(self): return self.mes.to_string().value else: if self.throw is not None: - from conversions import to_string + from .conversions import to_string return to_string(self.throw) else: return self.typ + ': ' + self.message diff --git a/js2py/internals/space.py b/js2py/internals/space.py index 7283070c..cb2e77ae 100644 --- a/js2py/internals/space.py +++ b/js2py/internals/space.py @@ -1,5 +1,5 @@ -from base import * -from simplex import * +from .base import * +from .simplex import * class Space(object): diff --git a/js2py/internals/trans_utils.py b/js2py/internals/trans_utils.py index 235b46a8..f99c0994 100644 --- a/js2py/internals/trans_utils.py +++ b/js2py/internals/trans_utils.py @@ -1,3 +1,10 @@ +import six +if six.PY3: + basestring = str + long = int + xrange = range + unicode = str + def to_key(literal_or_identifier): ''' returns string representation of this object''' if literal_or_identifier['type'] == 'Identifier': diff --git a/setup.py b/setup.py index bc086d1b..a1e871fe 100644 --- a/setup.py +++ b/setup.py @@ -23,11 +23,11 @@ -# python setup.py sdist bdist_wheel +# rm -rf dist build && python setup.py sdist bdist_wheel # twine upload dist/* setup( name='Js2Py', - version='0.58', + version='0.61', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', diff --git a/tests/run.py b/tests/run.py index 6f7deaea..b50894d2 100644 --- a/tests/run.py +++ b/tests/run.py @@ -11,7 +11,7 @@ TEST_TIMEOUT = 2 INCLUDE_PATH = 'includes/' -TEST_PATH = 'test_cases/language/arguments-object/' +TEST_PATH = 'test_cases/built-ins' # choose which JS runtime to test. Js2Py has 2 independent runtimes. Translation based (translator) and the vm interpreter based. @@ -93,7 +93,6 @@ def __init__(self, path): self.strict_only = False self.code = self.init + self.raw - print(self.code) def _parse_test_info(self): self.raw_info = re.search(r'/\*---(.+)---\*/', self.raw, re.DOTALL).groups()[0].strip() @@ -169,7 +168,7 @@ def run(self): except: full_error = traceback.format_exc() passed = False - reason = 'UNKNOWN - URGENT, FIX NOW!\n' + traceback.format_exc()+'\n\n' + reason = 'UNKNOWN - URGENT, FIX NOW!\n' + traceback.format_exc()[-2000:]+'\n\n' label = 'CRASHED' if not errors: @@ -189,7 +188,10 @@ def run(self): return passed, label, reason, full_error def print_result(self): - print(self.clear_name, self.es5id, self.label, self.reason, '\nFile "%s", line 1'%self.full_path if self.label=='CRASHED' else '') + #if self.label == "CRASHED": return + print(self.clear_name, self.es5id, self.label, self.reason, '\nFile "%s", line 1'%self.full_path if self.label!='PASSED' else '') + + if self.label == "CRASHED": print(self.code) or exit() @@ -207,14 +209,12 @@ def list_path(path, folders=False): try: return sorted(res, key=LooseVersion) except: - print('Fuck python 3!') return sorted(res) # python 3 def fest_all(path): files = list_path(path) folders = list_path(path, folders=True) for f in files: - print(f) if not f.endswith('.js'): continue try: diff --git a/tests/test_cases/language/statements/function/S13.2.1_A1_T1.js b/tests/test_cases/language/statements/function/S13.2.1_A1_T1.js index 5fb7a9a5..7ed84848 100755 --- a/tests/test_cases/language/statements/function/S13.2.1_A1_T1.js +++ b/tests/test_cases/language/statements/function/S13.2.1_A1_T1.js @@ -7,66 +7,3 @@ es5id: 13.2.1_A1_T1 description: Creating function calls 32 elements depth ---*/ -(function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){ - (function(){})() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() - })() -})() From 789af48b9635bc404f1a1b26f6e926735db679ee Mon Sep 17 00:00:00 2001 From: pro-src Date: Sat, 6 Apr 2019 20:44:19 -0500 Subject: [PATCH 38/77] Convert exponential numbers to string ecma-262/5.1/#sec-9.8.1 --- js2py/base.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js2py/base.py b/js2py/base.py index 67c80d59..73e23a69 100644 --- a/js2py/base.py +++ b/js2py/base.py @@ -5,6 +5,7 @@ from .translators.friendly_nodes import REGEXP_CONVERTER from .utils.injector import fix_js_args from types import FunctionType, ModuleType, GeneratorType, BuiltinFunctionType, MethodType, BuiltinMethodType +from math import floor, log10 import traceback try: import numpy @@ -608,6 +609,12 @@ def to_string(self): elif self.is_infinity(): sign = '-' if self.value < 0 else '' return Js(sign + 'Infinity') + elif self.value != 0 and not (-7 < floor(log10(abs(self.value))) < 21): + n = repr(float(self.value)).split('e') + # Remove leading zeros from exponent + e = int(n[1]) + + return Js(unicode(n[0] + ('e' if e < 0 else 'e+') + str(e))) elif isinstance(self.value, long) or self.value.is_integer(): # dont print .0 return Js(unicode(int(self.value))) From f6be867f1d1bd8b9f84d4de4812f91c123a227e5 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Thu, 18 Apr 2019 16:50:28 +0200 Subject: [PATCH 39/77] dtoa --- js2py/base.py | 18 +---- js2py/internals/conversions.py | 9 +-- js2py/internals/seval.py | 6 +- js2py/internals/simplex.py | 21 +++++- tests/run.py | 2 +- .../language/arguments-object/10.5-1-s.js | 25 ------- .../language/arguments-object/10.5-1gs.js | 19 ------ .../language/arguments-object/10.5-7-b-1-s.js | 24 ------- .../language/arguments-object/10.5-7-b-2-s.js | 24 ------- .../language/arguments-object/10.5-7-b-3-s.js | 26 -------- .../language/arguments-object/10.5-7-b-4-s.js | 27 -------- .../arguments-object/10.6-10-c-ii-1-s.js | 25 ------- .../arguments-object/10.6-10-c-ii-1.js | 22 ------- .../arguments-object/10.6-10-c-ii-2-s.js | 24 ------- .../arguments-object/10.6-10-c-ii-2.js | 23 ------- .../language/arguments-object/10.6-11-b-1.js | 58 ---------------- .../language/arguments-object/10.6-12-1.js | 22 ------- .../language/arguments-object/10.6-12-2.js | 23 ------- .../language/arguments-object/10.6-13-1.js | 22 ------- .../language/arguments-object/10.6-13-a-1.js | 48 -------------- .../language/arguments-object/10.6-13-a-2.js | 36 ---------- .../language/arguments-object/10.6-13-a-3.js | 37 ----------- .../arguments-object/10.6-13-b-1-s.js | 27 -------- .../arguments-object/10.6-13-b-2-s.js | 20 ------ .../arguments-object/10.6-13-b-3-s.js | 27 -------- .../arguments-object/10.6-13-c-1-s.js | 27 -------- .../arguments-object/10.6-13-c-2-s.js | 20 ------ .../arguments-object/10.6-13-c-3-s.js | 25 ------- .../language/arguments-object/10.6-14-1-s.js | 21 ------ .../arguments-object/10.6-14-b-1-s.js | 31 --------- .../arguments-object/10.6-14-b-4-s.js | 30 --------- .../arguments-object/10.6-14-c-1-s.js | 31 --------- .../arguments-object/10.6-14-c-4-s.js | 30 --------- .../language/arguments-object/10.6-1gs.js | 18 ----- .../language/arguments-object/10.6-2gs.js | 20 ------ .../language/arguments-object/10.6-5-1.js | 19 ------ .../language/arguments-object/10.6-6-1.js | 18 ----- .../language/arguments-object/10.6-6-2.js | 21 ------ .../language/arguments-object/10.6-6-3.js | 19 ------ .../language/arguments-object/10.6-6-4.js | 19 ------ .../language/arguments-object/10.6-7-1.js | 56 ---------------- .../arguments-object/S10.1.6_A1_T2.js | 33 ---------- .../arguments-object/S10.1.6_A1_T3.js | 20 ------ .../language/arguments-object/S10.6_A1.js | 34 ---------- .../language/arguments-object/S10.6_A2.js | 35 ---------- .../language/arguments-object/S10.6_A3_T1.js | 34 ---------- .../language/arguments-object/S10.6_A3_T2.js | 48 -------------- .../language/arguments-object/S10.6_A3_T3.js | 39 ----------- .../language/arguments-object/S10.6_A3_T4.js | 41 ------------ .../language/arguments-object/S10.6_A4.js | 37 ----------- .../language/arguments-object/S10.6_A5_T1.js | 34 ---------- .../language/arguments-object/S10.6_A5_T2.js | 48 -------------- .../language/arguments-object/S10.6_A5_T3.js | 38 ----------- .../language/arguments-object/S10.6_A5_T4.js | 40 ----------- .../language/arguments-object/S10.6_A6.js | 66 ------------------- .../language/arguments-object/S10.6_A7.js | 18 ----- 56 files changed, 27 insertions(+), 1558 deletions(-) delete mode 100755 tests/test_cases/language/arguments-object/10.5-1-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.5-1gs.js delete mode 100755 tests/test_cases/language/arguments-object/10.5-7-b-1-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.5-7-b-2-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.5-7-b-3-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.5-7-b-4-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-10-c-ii-1-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-10-c-ii-1.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-10-c-ii-2-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-10-c-ii-2.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-11-b-1.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-12-1.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-12-2.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-1.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-a-1.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-a-2.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-a-3.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-b-1-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-b-2-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-b-3-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-c-1-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-c-2-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-13-c-3-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-14-1-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-14-b-1-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-14-b-4-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-14-c-1-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-14-c-4-s.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-1gs.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-2gs.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-5-1.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-6-1.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-6-2.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-6-3.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-6-4.js delete mode 100755 tests/test_cases/language/arguments-object/10.6-7-1.js delete mode 100755 tests/test_cases/language/arguments-object/S10.1.6_A1_T2.js delete mode 100755 tests/test_cases/language/arguments-object/S10.1.6_A1_T3.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A1.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A2.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A3_T1.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A3_T2.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A3_T3.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A3_T4.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A4.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A5_T1.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A5_T2.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A5_T3.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A5_T4.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A6.js delete mode 100755 tests/test_cases/language/arguments-object/S10.6_A7.js diff --git a/js2py/base.py b/js2py/base.py index 73e23a69..cf1eca08 100644 --- a/js2py/base.py +++ b/js2py/base.py @@ -604,21 +604,7 @@ def to_string(self): elif typ == 'Boolean': return Js('true') if self.value else Js('false') elif typ == 'Number': #or self.Class=='Number': - if self.is_nan(): - return Js('NaN') - elif self.is_infinity(): - sign = '-' if self.value < 0 else '' - return Js(sign + 'Infinity') - elif self.value != 0 and not (-7 < floor(log10(abs(self.value))) < 21): - n = repr(float(self.value)).split('e') - # Remove leading zeros from exponent - e = int(n[1]) - - return Js(unicode(n[0] + ('e' if e < 0 else 'e+') + str(e))) - elif isinstance(self.value, - long) or self.value.is_integer(): # dont print .0 - return Js(unicode(int(self.value))) - return Js(unicode(self.value)) # accurate enough + return Js(unicode(js_dtoa(self.value))) elif typ == 'String': return self else: #object @@ -1053,7 +1039,7 @@ def PyJsComma(a, b): return b -from .internals.simplex import JsException as PyJsException +from .internals.simplex import JsException as PyJsException, js_dtoa import pyjsparser pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError('SyntaxError', msg) diff --git a/js2py/internals/conversions.py b/js2py/internals/conversions.py index 4b107215..8b2c7c30 100644 --- a/js2py/internals/conversions.py +++ b/js2py/internals/conversions.py @@ -73,14 +73,7 @@ def to_string(self): elif typ == 'Boolean': return 'true' if self else 'false' elif typ == 'Number': # or self.Class=='Number': - if is_nan(self): - return 'NaN' - elif is_infinity(self): - sign = '-' if self < 0 else '' - return sign + 'Infinity' - elif int(self) == self: # integer value! - return unicode(int(self)) - return unicode(self) # todo make it print exactly like node.js + return js_dtoa(self) else: # object return to_string(to_primitive(self, 'String')) diff --git a/js2py/internals/seval.py b/js2py/internals/seval.py index 147c9baf..cd8ea50f 100644 --- a/js2py/internals/seval.py +++ b/js2py/internals/seval.py @@ -14,8 +14,8 @@ def get_js_bytecode(js): a.emit(d) return a.exe.tape -def eval_js_vm(js, debug_mode=False): - a = ByteCodeGenerator(Code(debug_mode=debug_mode)) +def eval_js_vm(js, debug=False): + a = ByteCodeGenerator(Code(debug_mode=debug)) s = Space() a.exe.space = s s.exe = a.exe @@ -24,7 +24,7 @@ def eval_js_vm(js, debug_mode=False): a.emit(d) fill_space.fill_space(s, a) - if debug_mode: + if debug: from pprint import pprint pprint(a.exe.tape) print() diff --git a/js2py/internals/simplex.py b/js2py/internals/simplex.py index 0b07b2cc..f2fdbd3a 100644 --- a/js2py/internals/simplex.py +++ b/js2py/internals/simplex.py @@ -79,7 +79,7 @@ def is_callable(self): def is_infinity(self): - return self == float('inf') or self == -float('inf') + return self == Infinity or self == -Infinity def is_nan(self): @@ -135,3 +135,22 @@ def value_from_js_exception(js_exception, space): return js_exception.throw else: return space.NewError(js_exception.typ, js_exception.message) + + +def js_dtoa(number): + if is_nan(number): + return u'NaN' + elif is_infinity(number): + if number > 0: + return u'Infinity' + return u'-Infinity' + elif number == 0.: + return u'0' + elif abs(number) < 1e-6 or abs(number) >= 1e21: + frac, exponent = unicode(float(number)).split('e') + # Remove leading zeros from the exponent. + exponent = int(exponent) + return frac + ('e' if exponent < 0 else 'e+') + unicode(exponent) + elif isinstance(number, long) or number.is_integer(): # dont print .0 + return unicode(int(number)) + return unicode(number) # python representation should be equivalent. \ No newline at end of file diff --git a/tests/run.py b/tests/run.py index b50894d2..1e6f8958 100644 --- a/tests/run.py +++ b/tests/run.py @@ -11,7 +11,7 @@ TEST_TIMEOUT = 2 INCLUDE_PATH = 'includes/' -TEST_PATH = 'test_cases/built-ins' +TEST_PATH = 'test_cases/language' # choose which JS runtime to test. Js2Py has 2 independent runtimes. Translation based (translator) and the vm interpreter based. diff --git a/tests/test_cases/language/arguments-object/10.5-1-s.js b/tests/test_cases/language/arguments-object/10.5-1-s.js deleted file mode 100755 index 8d4f6947..00000000 --- a/tests/test_cases/language/arguments-object/10.5-1-s.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.5-1-s -description: Strict Mode - arguments object is immutable -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - try { - (function fun() { - eval("arguments = 10"); - })(30); - return false; - } catch (e) { - return (e instanceof SyntaxError); - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.5-1gs.js b/tests/test_cases/language/arguments-object/10.5-1gs.js deleted file mode 100755 index b7be5487..00000000 --- a/tests/test_cases/language/arguments-object/10.5-1gs.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.5-1gs -description: Strict Mode - arguments cannot be assigned to in a strict function -negative: SyntaxError -flags: [onlyStrict] ----*/ - -"use strict"; -throw NotEarlyError; - -function f_10_5_1_gs(){ - arguments = 7; -} diff --git a/tests/test_cases/language/arguments-object/10.5-7-b-1-s.js b/tests/test_cases/language/arguments-object/10.5-7-b-1-s.js deleted file mode 100755 index 6c5c1b0b..00000000 --- a/tests/test_cases/language/arguments-object/10.5-7-b-1-s.js +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.5-7-b-1-s -description: Strict Mode - arguments object is immutable in eval'ed functions -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - - try { - eval("(function _10_5_7_b_1_fun() { arguments = 10;} ());"); - return false; - } catch (e) { - return e instanceof SyntaxError; - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.5-7-b-2-s.js b/tests/test_cases/language/arguments-object/10.5-7-b-2-s.js deleted file mode 100755 index b443416e..00000000 --- a/tests/test_cases/language/arguments-object/10.5-7-b-2-s.js +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.5-7-b-2-s -description: Strict Mode - arguments object index assignment is allowed -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - - function _10_5_7_b_2_fun() { - arguments[7] = 12; - return arguments[7] === 12; - }; - - return _10_5_7_b_2_fun(30); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.5-7-b-3-s.js b/tests/test_cases/language/arguments-object/10.5-7-b-3-s.js deleted file mode 100755 index b9fc991e..00000000 --- a/tests/test_cases/language/arguments-object/10.5-7-b-3-s.js +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.5-7-b-3-s -description: > - Strict Mode - Adding property to the arguments object successful - under strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - - function _10_5_7_b_3_fun() { - arguments[1] = 12; - return arguments[0] === 30 && arguments[1] === 12; - }; - - return _10_5_7_b_3_fun(30); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.5-7-b-4-s.js b/tests/test_cases/language/arguments-object/10.5-7-b-4-s.js deleted file mode 100755 index 3f9f1ec6..00000000 --- a/tests/test_cases/language/arguments-object/10.5-7-b-4-s.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.5-7-b-4-s -description: > - Strict Mode - Deleting property of the arguments object successful - under strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - - function _10_5_7_b_4_fun() { - var _10_5_7_b_4_1 = arguments[0] === 30 && arguments[1] === 12; - delete arguments[1]; - var _10_5_7_b_4_2 = arguments[0] === 30 && typeof arguments[1] === "undefined"; - return _10_5_7_b_4_1 && _10_5_7_b_4_2; - }; - return _10_5_7_b_4_fun(30, 12); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-10-c-ii-1-s.js b/tests/test_cases/language/arguments-object/10.6-10-c-ii-1-s.js deleted file mode 100755 index 771355e5..00000000 --- a/tests/test_cases/language/arguments-object/10.6-10-c-ii-1-s.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-10-c-ii-1-s -description: > - arguments[i] remains same after changing actual parameters in - strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - function foo(a,b,c) - { - 'use strict'; - a = 1; b = 'str'; c = 2.1; - return (arguments[0] === 10 && arguments[1] === 'sss' && arguments[2] === 1); - } - return foo(10, 'sss', 1); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-10-c-ii-1.js b/tests/test_cases/language/arguments-object/10.6-10-c-ii-1.js deleted file mode 100755 index c9ad44df..00000000 --- a/tests/test_cases/language/arguments-object/10.6-10-c-ii-1.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-10-c-ii-1 -description: arguments[i] change with actual parameters -includes: [runTestCase.js] ----*/ - -function testcase() { - function foo(a,b,c) - { - a = 1; b = 'str'; c = 2.1; - if(arguments[0] === 1 && arguments[1] === 'str' && arguments[2] === 2.1) - return true; - } - return foo(10,'sss',1); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-10-c-ii-2-s.js b/tests/test_cases/language/arguments-object/10.6-10-c-ii-2-s.js deleted file mode 100755 index b39ec15b..00000000 --- a/tests/test_cases/language/arguments-object/10.6-10-c-ii-2-s.js +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-10-c-ii-2-s -description: arguments[i] doesn't map to actual parameters in strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - - function foo(a,b,c) - { - 'use strict'; - arguments[0] = 1; arguments[1] = 'str'; arguments[2] = 2.1; - return 10 === a && 'sss' === b && 1 === c; - } - return foo(10,'sss',1); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-10-c-ii-2.js b/tests/test_cases/language/arguments-object/10.6-10-c-ii-2.js deleted file mode 100755 index c1eee201..00000000 --- a/tests/test_cases/language/arguments-object/10.6-10-c-ii-2.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-10-c-ii-2 -description: arguments[i] map to actual parameter -includes: [runTestCase.js] ----*/ - -function testcase() { - - function foo(a,b,c) - { - arguments[0] = 1; arguments[1] = 'str'; arguments[2] = 2.1; - if(1 === a && 'str' === b && 2.1 === c) - return true; - } - return foo(10,'sss',1); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-11-b-1.js b/tests/test_cases/language/arguments-object/10.6-11-b-1.js deleted file mode 100755 index cd6949ce..00000000 --- a/tests/test_cases/language/arguments-object/10.6-11-b-1.js +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-11-b-1 -description: > - Arguments Object has index property '0' as its own property, it - shoulde be writable, enumerable, configurable and does not invoke - the setter defined on Object.prototype[0] (Step 11.b) -includes: [runTestCase.js] ----*/ - -function testcase() { - try { - var data = "data"; - var getFunc = function () { - return data; - }; - - var setFunc = function (value) { - data = value; - }; - - Object.defineProperty(Object.prototype, "0", { - get: getFunc, - set: setFunc, - configurable: true - }); - - var argObj = (function () { return arguments })(1); - - var verifyValue = false; - verifyValue = (argObj[0] === 1); - - var verifyEnumerable = false; - for (var p in argObj) { - if (p === "0" && argObj.hasOwnProperty("0")) { - verifyEnumerable = true; - } - } - - var verifyWritable = false; - argObj[0] = 1001; - verifyWritable = (argObj[0] === 1001); - - var verifyConfigurable = false; - delete argObj[0]; - verifyConfigurable = argObj.hasOwnProperty("0"); - - return verifyValue && verifyWritable && verifyEnumerable && !verifyConfigurable && data === "data"; - } finally { - delete Object.prototype[0]; - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-12-1.js b/tests/test_cases/language/arguments-object/10.6-12-1.js deleted file mode 100755 index 539b49c6..00000000 --- a/tests/test_cases/language/arguments-object/10.6-12-1.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-12-1 -description: Accessing callee property of Arguments object is allowed -includes: [runTestCase.js] ----*/ - -function testcase() { - try - { - arguments.callee; - return true; - } - catch (e) { - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-12-2.js b/tests/test_cases/language/arguments-object/10.6-12-2.js deleted file mode 100755 index 2376d2c4..00000000 --- a/tests/test_cases/language/arguments-object/10.6-12-2.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-12-2 -description: arguments.callee has correct attributes -includes: [runTestCase.js] ----*/ - -function testcase() { - - var desc = Object.getOwnPropertyDescriptor(arguments,"callee"); - if(desc.configurable === true && - desc.enumerable === false && - desc.writable === true && - desc.hasOwnProperty('get') == false && - desc.hasOwnProperty('put') == false) - return true; - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-1.js b/tests/test_cases/language/arguments-object/10.6-13-1.js deleted file mode 100755 index 3def632f..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-1.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-1 -description: Accessing caller property of Arguments object is allowed -includes: [runTestCase.js] ----*/ - -function testcase() { - try - { - arguments.caller; - return true; - } - catch (e) { - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-a-1.js b/tests/test_cases/language/arguments-object/10.6-13-a-1.js deleted file mode 100755 index 9bedd01e..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-a-1.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-a-1 -description: > - In non-strict mode, arguments object should have its own 'callee' - property defined (Step 13.a) -includes: [runTestCase.js] ----*/ - -function testcase() { - try { - Object.defineProperty(Object.prototype, "callee", { - value: 1, - writable: false, - configurable: true - }); - - var argObj = (function () { return arguments })(); - - var verifyValue = false; - verifyValue = typeof argObj.callee === "function"; - - var verifyWritable = false; - argObj.callee = 1001; - verifyWritable = (argObj.callee === 1001); - - var verifyEnumerable = false; - for (var p in argObj) { - if (p === "callee" && argObj.hasOwnProperty("callee")) { - verifyEnumerable = true; - } - } - - var verifyConfigurable = false; - delete argObj.callee; - verifyConfigurable = argObj.hasOwnProperty("callee"); - - return verifyValue && verifyWritable && !verifyEnumerable && !verifyConfigurable; - } finally { - delete Object.prototype.callee; - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-a-2.js b/tests/test_cases/language/arguments-object/10.6-13-a-2.js deleted file mode 100755 index 3cdce0d6..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-a-2.js +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-a-2 -description: A direct call to arguments.callee.caller should work -includes: [runTestCase.js] ----*/ - -function testcase() { - var called = false; - - function test1(flag) { - if (flag!==true) { - test2(); - } else { - called = true; - } - } - - function test2() { - if(arguments.callee.caller===undefined) { - called=true; // Extension not supported - fake it - } else { - arguments.callee.caller(true); - } - } - - test1(); - return called; -} - -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-a-3.js b/tests/test_cases/language/arguments-object/10.6-13-a-3.js deleted file mode 100755 index 86454f9c..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-a-3.js +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-a-3 -description: An indirect call to arguments.callee.caller should work -includes: [runTestCase.js] ----*/ - -function testcase() { - var called = false; - - function test1(flag) { - if (flag!==true) { - test2(); - } else { - called = true; - } - } - - function test2() { - if (arguments.callee.caller===undefined) { - called = true; //Extension not supported - fake it - } else { - var explicit = arguments.callee.caller; - explicit(true); - } - } - - test1(); - return called; -} - -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-b-1-s.js b/tests/test_cases/language/arguments-object/10.6-13-b-1-s.js deleted file mode 100755 index c2666194..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-b-1-s.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-b-1-s -description: > - Accessing caller property of Arguments object throws TypeError in - strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - 'use strict'; - try - { - arguments.caller; - } - catch (e) { - if(e instanceof TypeError) - return true; - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-b-2-s.js b/tests/test_cases/language/arguments-object/10.6-13-b-2-s.js deleted file mode 100755 index 8db28513..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-b-2-s.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-b-2-s -description: arguments.caller exists in strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - - 'use strict'; - var desc = Object.getOwnPropertyDescriptor(arguments,"caller"); - return desc!== undefined; - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-b-3-s.js b/tests/test_cases/language/arguments-object/10.6-13-b-3-s.js deleted file mode 100755 index c7b56413..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-b-3-s.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-b-3-s -description: arguments.caller is non-configurable in strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - - 'use strict'; - var desc = Object.getOwnPropertyDescriptor(arguments,"caller"); - - return (desc.configurable === false && - desc.enumerable === false && - desc.hasOwnProperty('value') == false && - desc.hasOwnProperty('writable') == false && - desc.hasOwnProperty('get') == true && - desc.hasOwnProperty('set') == true); - - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-c-1-s.js b/tests/test_cases/language/arguments-object/10.6-13-c-1-s.js deleted file mode 100755 index 4f25f14a..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-c-1-s.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-c-1-s -description: > - Accessing callee property of Arguments object throws TypeError in - strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - 'use strict'; - try - { - arguments.callee; - return false; - } - catch (e) { - return (e instanceof TypeError); - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-c-2-s.js b/tests/test_cases/language/arguments-object/10.6-13-c-2-s.js deleted file mode 100755 index 125852d3..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-c-2-s.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-c-2-s -description: arguments.callee is exists in strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - - 'use strict'; - var desc = Object.getOwnPropertyDescriptor(arguments,"callee"); - return desc !== undefined; - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-13-c-3-s.js b/tests/test_cases/language/arguments-object/10.6-13-c-3-s.js deleted file mode 100755 index 6182ad0f..00000000 --- a/tests/test_cases/language/arguments-object/10.6-13-c-3-s.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-13-c-3-s -description: arguments.callee is non-configurable in strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - - 'use strict'; - var desc = Object.getOwnPropertyDescriptor(arguments,"callee"); - return (desc.configurable === false && - desc.enumerable === false && - desc.hasOwnProperty('value') == false && - desc.hasOwnProperty('writable') == false && - desc.hasOwnProperty('get') == true && - desc.hasOwnProperty('set') == true); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-14-1-s.js b/tests/test_cases/language/arguments-object/10.6-14-1-s.js deleted file mode 100755 index f7f76465..00000000 --- a/tests/test_cases/language/arguments-object/10.6-14-1-s.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-14-1-s -description: Strict Mode - 'callee' exists and 'caller' exists under strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - var argObj = function () { - return arguments; - } (); - return argObj.hasOwnProperty("callee") && argObj.hasOwnProperty("caller"); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-14-b-1-s.js b/tests/test_cases/language/arguments-object/10.6-14-b-1-s.js deleted file mode 100755 index 488b443d..00000000 --- a/tests/test_cases/language/arguments-object/10.6-14-b-1-s.js +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-14-b-1-s -description: > - Strict Mode - [[Enumerable]] attribute value in 'caller' is false - under strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - - var argObj = function () { - return arguments; - } (); - - var verifyEnumerable = false; - for (var _10_6_14_b_1 in argObj) { - if (argObj.hasOwnProperty(_10_6_14_b_1) && _10_6_14_b_1 === "caller") { - verifyEnumerable = true; - } - } - return !verifyEnumerable && argObj.hasOwnProperty("caller"); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-14-b-4-s.js b/tests/test_cases/language/arguments-object/10.6-14-b-4-s.js deleted file mode 100755 index 8a8b54a7..00000000 --- a/tests/test_cases/language/arguments-object/10.6-14-b-4-s.js +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-14-b-4-s -description: > - Strict Mode - TypeError is thrown when accessing the [[Set]] - attribute in 'caller' under strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - - var argObj = function () { - return arguments; - } (); - - try { - argObj.caller = {}; - return false; - } catch (e) { - return e instanceof TypeError; - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-14-c-1-s.js b/tests/test_cases/language/arguments-object/10.6-14-c-1-s.js deleted file mode 100755 index 215e95db..00000000 --- a/tests/test_cases/language/arguments-object/10.6-14-c-1-s.js +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-14-c-1-s -description: > - Strict Mode - [[Enumerable]] attribute value in 'callee' is false - under strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - - var argObj = function () { - return arguments; - } (); - - var verifyEnumerable = false; - for (var _10_6_14_c_1 in argObj) { - if (argObj.hasOwnProperty(_10_6_14_c_1) && _10_6_14_c_1 === "callee") { - verifyEnumerable = true; - } - } - return !verifyEnumerable && argObj.hasOwnProperty("callee"); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-14-c-4-s.js b/tests/test_cases/language/arguments-object/10.6-14-c-4-s.js deleted file mode 100755 index 9f2baa39..00000000 --- a/tests/test_cases/language/arguments-object/10.6-14-c-4-s.js +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-14-c-4-s -description: > - Strict Mode - TypeError is thrown when accessing the [[Set]] - attribute in 'callee' under strict mode -flags: [onlyStrict] -includes: [runTestCase.js] ----*/ - -function testcase() { - "use strict"; - - var argObj = function () { - return arguments; - } (); - - try { - argObj.callee = {}; - return false; - } catch (e) { - return e instanceof TypeError; - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-1gs.js b/tests/test_cases/language/arguments-object/10.6-1gs.js deleted file mode 100755 index b4bf6b28..00000000 --- a/tests/test_cases/language/arguments-object/10.6-1gs.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-1gs -description: > - Strict Mode - arguments.callee cannot be accessed in a strict - function, but does not throw an early error -flags: [onlyStrict] ----*/ - -"use strict"; -function f_10_6_1_gs(){ - return arguments.callee; -} diff --git a/tests/test_cases/language/arguments-object/10.6-2gs.js b/tests/test_cases/language/arguments-object/10.6-2gs.js deleted file mode 100755 index d5782511..00000000 --- a/tests/test_cases/language/arguments-object/10.6-2gs.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-2gs -description: > - Strict Mode - arguments.callee cannot be accessed in a strict - function -negative: . -flags: [onlyStrict] ----*/ - -"use strict"; -function f_10_6_1_gs(){ - return arguments.callee; -} -f_10_6_1_gs(); diff --git a/tests/test_cases/language/arguments-object/10.6-5-1.js b/tests/test_cases/language/arguments-object/10.6-5-1.js deleted file mode 100755 index 058a96c7..00000000 --- a/tests/test_cases/language/arguments-object/10.6-5-1.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-5-1 -description: > - [[Prototype]] property of Arguments is set to Object prototype - object -includes: [runTestCase.js] ----*/ - -function testcase() { - if(Object.getPrototypeOf(arguments) === Object.getPrototypeOf({})) - return true; - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-6-1.js b/tests/test_cases/language/arguments-object/10.6-6-1.js deleted file mode 100755 index 3ac99bd3..00000000 --- a/tests/test_cases/language/arguments-object/10.6-6-1.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-6-1 -description: "'length property of arguments object exists" -includes: [runTestCase.js] ----*/ - -function testcase() { - - var desc = Object.getOwnPropertyDescriptor(arguments,"length"); - return desc !== undefined - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-6-2.js b/tests/test_cases/language/arguments-object/10.6-6-2.js deleted file mode 100755 index 6a1f56e4..00000000 --- a/tests/test_cases/language/arguments-object/10.6-6-2.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-6-2 -description: "'length' property of arguments object has correct attributes" -includes: [runTestCase.js] ----*/ - -function testcase() { - - var desc = Object.getOwnPropertyDescriptor(arguments,"length"); - if(desc.configurable === true && - desc.enumerable === false && - desc.writable === true ) - return true; - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-6-3.js b/tests/test_cases/language/arguments-object/10.6-6-3.js deleted file mode 100755 index bd461a05..00000000 --- a/tests/test_cases/language/arguments-object/10.6-6-3.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-6-3 -description: > - 'length' property of arguments object for 0 argument function - exists -includes: [runTestCase.js] ----*/ - -function testcase() { - var arguments= undefined; - return (function () {return arguments.length !== undefined})(); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-6-4.js b/tests/test_cases/language/arguments-object/10.6-6-4.js deleted file mode 100755 index 1ece6916..00000000 --- a/tests/test_cases/language/arguments-object/10.6-6-4.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-6-4 -description: > - 'length' property of arguments object for 0 argument function call - is 0 even with formal parameters -includes: [runTestCase.js] ----*/ - -function testcase() { - var arguments= undefined; - return (function (a,b,c) {return arguments.length === 0})(); - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/10.6-7-1.js b/tests/test_cases/language/arguments-object/10.6-7-1.js deleted file mode 100755 index ca2238d4..00000000 --- a/tests/test_cases/language/arguments-object/10.6-7-1.js +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2012 Ecma International. All rights reserved. -// Ecma International makes this code available under the terms and conditions set -// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the -// "Use Terms"). Any redistribution of this code must retain the above -// copyright and this notice and otherwise comply with the Use Terms. - -/*--- -es5id: 10.6-7-1 -description: > - Arguments Object has length as its own property and does not - invoke the setter defined on Object.prototype.length (Step 7) -includes: [runTestCase.js] ----*/ - -function testcase() { - try { - var data = "data"; - var getFunc = function () { - return 12; - }; - - var setFunc = function (value) { - data = value; - }; - - Object.defineProperty(Object.prototype, "length", { - get: getFunc, - set: setFunc, - configurable: true - }); - - var verifyValue = false; - var argObj = (function () { return arguments })(); - verifyValue = (argObj.length === 0); - - var verifyWritable = false; - argObj.length = 1001; - verifyWritable = (argObj.length === 1001); - - var verifyEnumerable = false; - for (var p in argObj) { - if (p === "length") { - verifyEnumerable = true; - } - } - - var verifyConfigurable = false; - delete argObj.length; - verifyConfigurable = argObj.hasOwnProperty("length"); - - return verifyValue && verifyWritable && !verifyEnumerable && !verifyConfigurable && data === "data"; - } finally { - delete Object.prototype.length; - } - } -runTestCase(testcase); diff --git a/tests/test_cases/language/arguments-object/S10.1.6_A1_T2.js b/tests/test_cases/language/arguments-object/S10.1.6_A1_T2.js deleted file mode 100755 index 89e5ec69..00000000 --- a/tests/test_cases/language/arguments-object/S10.1.6_A1_T2.js +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - The activation object is initialised with a property with name arguments - and attributes {DontDelete} -es5id: 10.1.6_A1_T2 -description: Checking funtion which returns property "arguments" ----*/ - -var ARG_STRING = "value of the argument property"; - -function f1() { - this.constructor.prototype.arguments = ARG_STRING; - return arguments; -} - -//CHECK#1 -if ((new f1(1,2,3,4,5)).length !== 5) - $ERROR('#1: (new f1(1,2,3,4,5)).length===5, where f1 returns "arguments" that is set to "'+ ARG_STRING + '"'); - -//CHECK#2 -if ((new f1(1,2,3,4,5))[3] !== 4) - $ERROR('#2: (new f1(1,2,3,4,5))[3]===4, where f1 returns "arguments" that is set to "'+ ARG_STRING + '"'); - -//CHECK#3 -var x = new f1(1,2,3,4,5); -if (delete x[3] !== true) - $ERROR('#3.1: Function parameters have attribute {DontDelete}'); - -if (x[3] === 4) - $ERROR('#3.2: Function parameters have attribute {DontDelete}'); diff --git a/tests/test_cases/language/arguments-object/S10.1.6_A1_T3.js b/tests/test_cases/language/arguments-object/S10.1.6_A1_T3.js deleted file mode 100755 index 4349aa4a..00000000 --- a/tests/test_cases/language/arguments-object/S10.1.6_A1_T3.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - The activation object is initialised with a property with name arguments - and attributes {DontDelete} -es5id: 10.1.6_A1_T3 -description: Checking function which returns "this" -flags: [noStrict] ----*/ - -function f1() { - if (delete arguments) { - $ERROR("#1: Function parameters have attribute {DontDelete}" + arguments); - } - return arguments; -} - -f1(); diff --git a/tests/test_cases/language/arguments-object/S10.6_A1.js b/tests/test_cases/language/arguments-object/S10.6_A1.js deleted file mode 100755 index e1ff7841..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A1.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - When control enters an execution context for function code, - an arguments object is created and initialised -es5id: 10.6_A1 -description: Executing function which uses arguments object ----*/ - -//CHECK#1 -function f1(){ - return arguments; -} - -try{ - var x = f1(); -} -catch(e){ - $ERROR("#1: arguments doesn't exists"); -} - -//CHECK#2 -var f2 = function(){ - return arguments; -} - -try{ - var x = f2(); -} -catch(e){ - $ERROR("#2: arguments doesn't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A2.js b/tests/test_cases/language/arguments-object/S10.6_A2.js deleted file mode 100755 index f5b4d777..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A2.js +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - The value of the internal [[Prototype]] property of the - created arguments object is the original Object prototype object, the one - that is the initial value of Object.prototype -es5id: 10.6_A2 -description: Checking arguments.constructor.prototype===Object.prototype ----*/ - -//CHECK#1 -function f1(){ - return arguments.constructor.prototype; -} -try{ - if(f1() !== Object.prototype){ - $ERROR('#1: arguments.constructor.prototype === Object.prototype'); - } -} -catch(e){ - $ERROR("#1: arguments doesn't exists"); -} - -//CHECK#2 -var f2 = function(){return arguments.constructor.prototype;}; -try{ - if(f2() !== Object.prototype){ - $ERROR('#2: arguments.constructor.prototype === Object.prototype'); - } -} -catch(e){ - $ERROR("#2: arguments doesn't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A3_T1.js b/tests/test_cases/language/arguments-object/S10.6_A3_T1.js deleted file mode 100755 index 8e4ad5c7..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A3_T1.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - A property is created with name callee with property - attributes { DontEnum } and no others -es5id: 10.6_A3_T1 -description: Checking existence of arguments.callee property ----*/ - -//CHECK#1 -function f1(){ - return arguments.hasOwnProperty("callee"); -} -try{ - if(f1() !== true){ - $ERROR("#1: arguments object doesn't contains property 'callee'"); - } -} -catch(e){ - $ERROR("#1: arguments object doesn't exists"); -} - -//CHECK#2 -var f2 = function(){return arguments.hasOwnProperty("callee");}; -try{ - if(f2() !== true){ - $ERROR("#2: arguments object doesn't contains property 'callee'"); - } -} -catch(e){ - $ERROR("#2: arguments object doesn't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A3_T2.js b/tests/test_cases/language/arguments-object/S10.6_A3_T2.js deleted file mode 100755 index d06369d6..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A3_T2.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - A property is created with name callee with property - attributes { DontEnum } and no others -es5id: 10.6_A3_T2 -description: Checking if enumerating the arguments.callee property fails ----*/ - -//CHECK#1 -function f1(){ - for(var x in arguments){ - if (x === "callee"){ - return false; - } - } - return true; -} - -try{ - if(!f1()){ - $ERROR("#1: A property callee don't have attribute { DontEnum }"); - } -} -catch(e){ - $ERROR("#1: arguments object don't exists"); -} - -//CHECK#2 -var f2 = function(){ - for(var x in arguments){ - if (x === "callee"){ - return false; - } - } - return true; -} - -try{ - if(!f2()){ - $ERROR("#2: A property callee don't have attribute { DontEnum }"); - } -} -catch(e){ - $ERROR("#2: arguments object don't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A3_T3.js b/tests/test_cases/language/arguments-object/S10.6_A3_T3.js deleted file mode 100755 index 3d735cd3..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A3_T3.js +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - A property is created with name callee with property - attributes { DontEnum } and no others -es5id: 10.6_A3_T3 -description: Checking if deleting arguments.callee property fails -flags: [noStrict] ----*/ - -//CHECK#1 -function f1(){ - return (delete arguments.callee); -} - -try{ - if(!f1()){ - $ERROR("#1: A property callee have attribute { DontDelete }"); - } -} -catch(e){ - $ERROR("#1: arguments object don't exists"); -} - -//CHECK#2 -var f2 = function(){ - return (delete arguments.callee); -} - -try{ - if(!f2()){ - $ERROR("#2: A property callee have attribute { DontDelete }"); - } -} -catch(e){ - $ERROR("#2: arguments object don't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A3_T4.js b/tests/test_cases/language/arguments-object/S10.6_A3_T4.js deleted file mode 100755 index 6be7774f..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A3_T4.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - A property is created with name callee with property - attributes { DontEnum } and no others -es5id: 10.6_A3_T4 -description: Overriding arguments.callee property -flags: [noStrict] ----*/ - -var str = "something different"; -//CHECK#1 -function f1(){ - arguments.callee = str; - return arguments; -} - -try{ - if(f1().callee !== str){ - $ERROR("#1: A property callee have attribute { ReadOnly }"); - } -} -catch(e){ - $ERROR("#1: arguments object don't exists"); -} - -//CHECK#2 -var f2 = function(){ - arguments.callee = str; - return arguments; - } -try{ - if(f2().callee !== str){ - $ERROR("#2: A property callee have attribute { ReadOnly }"); - } -} -catch(e){ - $ERROR("#2: arguments object don't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A4.js b/tests/test_cases/language/arguments-object/S10.6_A4.js deleted file mode 100755 index 0ed7598b..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A4.js +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - The initial value of the created property callee is the - Function object being executed -es5id: 10.6_A4 -description: Checking that arguments.callee === function object -flags: [noStrict] ----*/ - -//CHECK#1 -function f1(){ - return arguments.callee; -} - -try{ - if(f1 !== f1()){ - $ERROR('#1: arguments.callee === f1'); - } -} -catch(e){ - $ERROR("#1: arguments object doesn't exists"); -} - -//CHECK#2 -var f2 = function(){return arguments.callee;}; - -try{ - if(f2 !== f2()){ - $ERROR('#2: arguments.callee === f2'); - } -} -catch(e){ - $ERROR("#1: arguments object doesn't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A5_T1.js b/tests/test_cases/language/arguments-object/S10.6_A5_T1.js deleted file mode 100755 index df172978..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A5_T1.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - A property is created with name length with property - attributes { DontEnum } and no others -es5id: 10.6_A5_T1 -description: Checking existence of arguments.length property ----*/ - -//CHECK#1 -function f1(){ - return arguments.hasOwnProperty("length"); -} -try{ - if(f1() !== true){ - $ERROR("#1: arguments object doesn't contains property 'length'"); - } -} -catch(e){ - $ERROR("#1: arguments object doesn't exists"); -} - -//CHECK#2 -var f2 = function(){return arguments.hasOwnProperty("length");}; -try{ - if(f2() !== true){ - $ERROR("#2: arguments object doesn't contains property 'length'"); - } -} -catch(e){ - $ERROR("#2: arguments object doesn't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A5_T2.js b/tests/test_cases/language/arguments-object/S10.6_A5_T2.js deleted file mode 100755 index 47027610..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A5_T2.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - A property is created with name length with property - attributes { DontEnum } and no others -es5id: 10.6_A5_T2 -description: Checking if enumerating the arguments.length property fails ----*/ - -//CHECK#1 -function f1(){ - for(var x in arguments){ - if (x === "length"){ - return false; - } - } - return true; -} - -try{ - if(!f1()){ - $ERROR("#1: A property length don't have attribute { DontEnum }"); - } -} -catch(e){ - $ERROR("#1: arguments object don't exists"); -} - -//CHECK#2 -var f2 = function(){ - for(var x in arguments){ - if (x === "length"){ - return false; - } - } - return true; -} - -try{ - if(!f2()){ - $ERROR("#2: A property length don't have attribute { DontEnum }"); - } -} -catch(e){ - $ERROR("#2: arguments object don't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A5_T3.js b/tests/test_cases/language/arguments-object/S10.6_A5_T3.js deleted file mode 100755 index b2859558..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A5_T3.js +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - A property is created with name length with property - attributes { DontEnum } and no others -es5id: 10.6_A5_T3 -description: Checking if deleting arguments.length property fails ----*/ - -//CHECK#1 -function f1(){ - return (delete arguments.length); -} - -try{ - if(!f1()){ - $ERROR("#1: A property length have attribute { DontDelete }"); - } -} -catch(e){ - $ERROR("#1: arguments object don't exists"); -} - -//CHECK#2 -var f2 = function(){ - return (delete arguments.length); -} - -try{ - if(!f2()){ - $ERROR("#2: A property length have attribute { DontDelete }"); - } -} -catch(e){ - $ERROR("#2: arguments object don't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A5_T4.js b/tests/test_cases/language/arguments-object/S10.6_A5_T4.js deleted file mode 100755 index 4336f11c..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A5_T4.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - A property is created with name length with property - attributes { DontEnum } and no others -es5id: 10.6_A5_T4 -description: Overriding arguments.length property ----*/ - -var str = "something different"; -//CHECK#1 -function f1(){ - arguments.length = str; - return arguments; -} - -try{ - if(f1().length !== str){ - $ERROR("#1: A property length have attribute { ReadOnly }"); - } -} -catch(e){ - $ERROR("#1: arguments object don't exists"); -} - -//CHECK#2 -var f2 = function(){ - arguments.length = str; - return arguments; - }; -try{ - if(f2().length !== str){ - $ERROR("#2: A property length have attribute { ReadOnly }"); - } -} -catch(e){ - $ERROR("#2: arguments object don't exists"); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A6.js b/tests/test_cases/language/arguments-object/S10.6_A6.js deleted file mode 100755 index e0eaf30a..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A6.js +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: > - The initial value of the created property length is the number - of actual parameter values supplied by the caller -es5id: 10.6_A6 -description: Create function, that returned arguments.length ----*/ - -function f1(){ - return arguments.length; -} - -//CHECK#1 -if(!(f1() === 0)){ - $ERROR('#1: argument.length === 0'); -} - -//CHECK#2 -if(!(f1(0) === 1)){ - $ERROR('#2: argument.length === 1'); -} - -//CHECK#3 -if(!(f1(0, 1) === 2)){ - $ERROR('#3: argument.length === 2'); -} - -//CHECK#4 -if(!(f1(0, 1, 2) === 3)){ - $ERROR('#4: argument.length === 3'); -} - -//CHECK#5 -if(!(f1(0, 1, 2, 3) === 4)){ - $ERROR('#5: argument.length === 4'); -} - -var f2 = function(){return arguments.length;}; - -//CHECK#6 -if(!(f2() === 0)){ - $ERROR('#6: argument.length === 0'); -} - -//CHECK#7 -if(!(f2(0) === 1)){ - $ERROR('#7: argument.length === 1'); -} - -//CHECK#8 -if(!(f2(0, 1) === 2)){ - $ERROR('#8: argument.length === 2'); -} - -//CHECK#9 -if(!(f2(0, 1, 2) === 3)){ - $ERROR('#9: argument.length === 3'); -} - -//CHECK#10 -if(!(f2(0, 1, 2, 3) === 4)){ - $ERROR('#10: argument.length === 4'); -} diff --git a/tests/test_cases/language/arguments-object/S10.6_A7.js b/tests/test_cases/language/arguments-object/S10.6_A7.js deleted file mode 100755 index 7012e9f9..00000000 --- a/tests/test_cases/language/arguments-object/S10.6_A7.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2009 the Sputnik authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -info: Get arguments of function -es5id: 10.6_A7 -description: Use property arguments ----*/ - -function f1() { - return arguments; -} - -//CHECK#1-5 -for(var i = 1; i < 5; i++){ -if (f1(1,2,3,4,5)[i] !== (i+1)) - $ERROR("#"+i+": Returning function's arguments work wrong, f1(1,2,3,4,5)["+i+"] !== "+(i+1)); -} From ba37b4ba11eddcebe1eded3a37df962f385b3f8e Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sat, 20 Apr 2019 22:19:37 +0200 Subject: [PATCH 40/77] Fix inconsistency between py2 and py3 jsdtoa --- js2py/internals/simplex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js2py/internals/simplex.py b/js2py/internals/simplex.py index f2fdbd3a..40dc7b82 100644 --- a/js2py/internals/simplex.py +++ b/js2py/internals/simplex.py @@ -147,10 +147,10 @@ def js_dtoa(number): elif number == 0.: return u'0' elif abs(number) < 1e-6 or abs(number) >= 1e21: - frac, exponent = unicode(float(number)).split('e') + frac, exponent = unicode(repr(float(number))).split('e') # Remove leading zeros from the exponent. exponent = int(exponent) return frac + ('e' if exponent < 0 else 'e+') + unicode(exponent) elif isinstance(number, long) or number.is_integer(): # dont print .0 return unicode(int(number)) - return unicode(number) # python representation should be equivalent. \ No newline at end of file + return unicode(repr(number)) # python representation should be equivalent. From c0fa43f5679cf91ca8986c5747fcb07a433dc584 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sat, 20 Apr 2019 22:55:15 +0200 Subject: [PATCH 41/77] fix 1e-4 1e-6 range --- js2py/internals/simplex.py | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/js2py/internals/simplex.py b/js2py/internals/simplex.py index 40dc7b82..4cd247eb 100644 --- a/js2py/internals/simplex.py +++ b/js2py/internals/simplex.py @@ -151,6 +151,10 @@ def js_dtoa(number): # Remove leading zeros from the exponent. exponent = int(exponent) return frac + ('e' if exponent < 0 else 'e+') + unicode(exponent) + elif abs(number) < 1e-4: # python starts to return exp notation while we still want the prec + frac, exponent = unicode(repr(float(number))).split('e-') + base = u'0.' + u'0' * (int(exponent) - 1) + frac.lstrip('-').replace('.', '') + return base if number > 0. else u'-' + base elif isinstance(number, long) or number.is_integer(): # dont print .0 return unicode(int(number)) return unicode(repr(number)) # python representation should be equivalent. diff --git a/setup.py b/setup.py index a1e871fe..79dab3e2 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.61', + version='0.63', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', From b5fbaafddf41f4efc1a4841a8a35f2fda094e60a Mon Sep 17 00:00:00 2001 From: SpencerPark Date: Wed, 1 May 2019 16:38:36 -0400 Subject: [PATCH 42/77] Fix injected local 'arguments' not working in list comprehension in bind. --- js2py/prototypes/jsfunction.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js2py/prototypes/jsfunction.py b/js2py/prototypes/jsfunction.py index f9598a31..2ed417e0 100644 --- a/js2py/prototypes/jsfunction.py +++ b/js2py/prototypes/jsfunction.py @@ -6,8 +6,6 @@ xrange = range unicode = str -# todo fix apply and bind - class FunctionPrototype: def toString(): @@ -41,6 +39,7 @@ def apply(): return this.call(obj, args) def bind(thisArg): + arguments_ = arguments target = this if not target.is_callable(): raise this.MakeError( @@ -48,5 +47,5 @@ def bind(thisArg): if len(arguments) <= 1: args = () else: - args = tuple([arguments[e] for e in xrange(1, len(arguments))]) + args = tuple([arguments_[e] for e in xrange(1, len(arguments_))]) return this.PyJsBoundFunction(target, thisArg, args) From efbfccabfb6452000bbb199fb492e689b0196574 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Tue, 14 May 2019 20:14:54 +0200 Subject: [PATCH 43/77] fix object initialized too early case --- js2py/translators/translating_nodes.py | 39 ++++++++++++++++---------- setup.py | 2 +- simple_test.py | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/js2py/translators/translating_nodes.py b/js2py/translators/translating_nodes.py index 0ae93dd9..371c8ede 100644 --- a/js2py/translators/translating_nodes.py +++ b/js2py/translators/translating_nodes.py @@ -218,27 +218,36 @@ def ArrayExpression(type, elements): # todo fix null inside problem def ObjectExpression(type, properties): - name = inline_stack.require('Object') + name = None elems = [] after = '' for p in properties: if p['kind'] == 'init': elems.append('%s:%s' % Property(**p)) - elif 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') - obj = '%s = Js({%s})\n' % (name, ','.join(elems)) - inline_stack.define(name, obj + after) - return name + 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): diff --git a/setup.py b/setup.py index 79dab3e2..7aacb045 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.63', + version='0.64', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', diff --git a/simple_test.py b/simple_test.py index fe4701e7..cf6fd74f 100644 --- a/simple_test.py +++ b/simple_test.py @@ -13,7 +13,7 @@ x, c = js2py.run_file('examples/esprima.js') assert c.esprima.parse('var abc = 40').to_dict() == {'type': 'Program', 'body': [{'type': 'VariableDeclaration', 'kind': 'var', 'declarations': [{'id': {'type': 'Identifier', 'name': 'abc'}, 'type': 'VariableDeclarator', 'init': {'type': 'Literal', 'raw': '40', 'value': 40}}]}], 'sourceType': 'script'} - +assert js2py.eval_js('var x = {get y() { return this.val;}, val: 11};x.y') == 11 try: assert js2py.eval_js('syntax error!') and 0 except js2py.PyJsException as err: From 74d465285baef8cf3bc6b94e5f93d2409813310a Mon Sep 17 00:00:00 2001 From: Petter Strandmark Date: Sat, 15 Jun 2019 21:11:10 +0200 Subject: [PATCH 44/77] Fix injector for Python 3.8+. --- js2py/utils/injector.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/js2py/utils/injector.py b/js2py/utils/injector.py index dd714a48..ea236d5e 100644 --- a/js2py/utils/injector.py +++ b/js2py/utils/injector.py @@ -115,7 +115,16 @@ def append_arguments(code_obj, new_locals): code_obj.co_freevars, code_obj.co_cellvars) # Done modifying codestring - make the code object - return types.CodeType(*args) + 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): From d7a03215a77ad51602a93466587f76eb0038b854 Mon Sep 17 00:00:00 2001 From: Petter Strandmark Date: Sun, 16 Jun 2019 12:59:17 +0200 Subject: [PATCH 45/77] Try to add Python 3.7 and 3.8 (dev). https://docs.travis-ci.com/user/languages/python/ --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 .travis.yml diff --git a/.travis.yml b/.travis.yml old mode 100644 new mode 100755 index 792002cb..5868181d --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,8 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" + - "3.8-dev" # command to install dependencies! install: "pip install -r requirements.txt && pip install numpy" # command to run tests! From a59d743fb742c670b7c825b4d0efc746385ad76f Mon Sep 17 00:00:00 2001 From: Petter Strandmark Date: Sun, 16 Jun 2019 13:04:46 +0200 Subject: [PATCH 46/77] Upgrade Travis CI to a xenial image. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5868181d..bb75b96f 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: xenial language: python python: - "2.6" From 80bc087d7403f3a604e905ad0cd8036d6897ae64 Mon Sep 17 00:00:00 2001 From: Petter Strandmark Date: Sun, 16 Jun 2019 13:10:33 +0200 Subject: [PATCH 47/77] Drop support for unsupported Python versions. https://devguide.python.org/#status-of-python-branches --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index bb75b96f..7b111cbd 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,7 @@ dist: xenial language: python python: - - "2.6" - "2.7" - - "3.3" - - "3.4" - "3.5" - "3.6" - "3.7" From 7858d1d0898070b8a9bce22c0c740a25b292f426 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sun, 16 Jun 2019 23:37:41 +0200 Subject: [PATCH 48/77] add require support from within js, fix browserify issue --- README.md | 130 ++++++++++++++++++++++++++++++++++--------- js2py/evaljs.py | 30 +++++++--- js2py/node_import.py | 50 ++++++++++++++--- 3 files changed, 168 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index cd7a9997..dc72d685 100644 --- a/README.md +++ b/README.md @@ -64,15 +64,10 @@ 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. -### JavaScript 'VirtualMachine' in Python - -If the translator for some reason does not work for you, you can try the new JS VM: - -```python -from js2py.internals import seval -seval.eval_js_vm(code) -``` +#### Installation + pip install js2py +
#### More advanced usage example @@ -110,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')") +``` +
+ +### 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). +
#### Limitations -It has only 3 known limitations: +There are 3 main limitations:
  • "strict mode" is ignored
  • with statement is not supported
  • Indirect call to eval is treated as direct call to eval (hence always evals in local scope)
-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')`.
-#### Installation - pip install js2py - -
#### Other Examples @@ -180,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)) ```
diff --git a/js2py/evaljs.py b/js2py/evaljs.py index 64eea5c4..ef4d7d95 100644 --- a/js2py/evaljs.py +++ b/js2py/evaljs.py @@ -128,26 +128,40 @@ def translate_js6(js): class EvalJs(object): """This class supports continuous execution of javascript under same context. - >>> js = EvalJs() - >>> js.execute('var a = 10;function f(x) {return x*x};') - >>> js.f(9) + >>> ctx = EvalJs() + >>> ctx.execute('var a = 10;function f(x) {return x*x};') + >>> ctx.f(9) 81 - >>> js.a + >>> ctx.a 10 context is a python dict or object that contains python variables that should be available to JavaScript For example: - >>> js = EvalJs({'a': 30}) - >>> js.execute('var x = a') - >>> js.x + >>> 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={}): + 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__ diff --git a/js2py/node_import.py b/js2py/node_import.py index a49a1f51..605c4b3a 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -1,6 +1,6 @@ __all__ = ['require'] import subprocess, os, codecs, glob -from .evaljs import translate_js +from .evaljs import translate_js, DEFAULT_HEADER import six DID_INIT = False DIRNAME = os.path.dirname(os.path.abspath(__file__)) @@ -15,7 +15,7 @@ def _init(): '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' + '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' @@ -46,12 +46,18 @@ def _init(): ''' +def _get_module_py_name(module_name): + return module_name.replace('-', '_') -def require(module_name, include_polyfill=False, update=False): +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 = module_name.replace('-', '_') + py_name = _get_module_py_name(module_name) module_filename = '%s.py' % py_name - var_name = py_name.rpartition('/')[-1] + 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() @@ -77,7 +83,7 @@ def require(module_name, include_polyfill=False, update=False): # convert the module assert subprocess.call( - '''node -e "(require('browserify')('./%s').bundle(function (err,data) {fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"''' + '''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, @@ -88,7 +94,8 @@ def require(module_name, include_polyfill=False, update=False): "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) @@ -107,7 +114,32 @@ def require(module_name, include_polyfill=False, update=False): 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! - context = {} + :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'][var_name].to_py() + return context['var'][_get_module_var_name(module_name)].to_py() From 5709e2936e5f910cab82e089b8606ab86d1aeb80 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Tue, 24 Dec 2019 13:06:41 +0100 Subject: [PATCH 49/77] fix rare py3 error --- js2py/internals/constructors/jsfunction.py | 2 +- js2py/test_internals.py | 9 --------- setup.py | 2 +- simple_test.py | 3 +++ 4 files changed, 5 insertions(+), 11 deletions(-) delete mode 100644 js2py/test_internals.py diff --git a/js2py/internals/constructors/jsfunction.py b/js2py/internals/constructors/jsfunction.py index d62731ac..738554a0 100644 --- a/js2py/internals/constructors/jsfunction.py +++ b/js2py/internals/constructors/jsfunction.py @@ -7,7 +7,7 @@ def Function(this, args): # convert arguments to python list of strings - a = map(to_string, tuple(args)) + a = list(map(to_string, tuple(args))) _body = u';' _args = () if len(a): diff --git a/js2py/test_internals.py b/js2py/test_internals.py deleted file mode 100644 index 12cf4ad7..00000000 --- a/js2py/test_internals.py +++ /dev/null @@ -1,9 +0,0 @@ -from internals import byte_trans -from internals import seval -import pyjsparser - -x = r''' -function g() {var h123 = 11; return [function g1() {return h123}, new Function('return h123')]} -g()[1]() -''' -print seval.eval_js_vm(x) diff --git a/setup.py b/setup.py index 7aacb045..905ebce2 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.64', + version='0.67', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', diff --git a/simple_test.py b/simple_test.py index cf6fd74f..b8ce6657 100644 --- a/simple_test.py +++ b/simple_test.py @@ -155,3 +155,6 @@ class Shape { ''').x == 2 print("Passed ECMA 6!") + +from js2py.internals import seval +assert seval.eval_js_vm("function k() {try {throw 3+8} catch (e) {console.log(e);return e}};k()", debug=True) == 11.0 \ No newline at end of file From 12739ed5978953f16fb5201a98c026bdb6fc1003 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Tue, 24 Dec 2019 13:21:41 +0100 Subject: [PATCH 50/77] fix babel travis --- setup.py | 2 +- simple_test.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index 905ebce2..af75c2cb 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ -# rm -rf dist build && python setup.py sdist bdist_wheel +# rm -rf dist build && python3 setup.py sdist bdist_wheel # twine upload dist/* setup( name='Js2Py', diff --git a/simple_test.py b/simple_test.py index b8ce6657..ccb1bd98 100644 --- a/simple_test.py +++ b/simple_test.py @@ -107,14 +107,14 @@ def get_bar(self): # chalk ( https://github.com/chalk/chalk ) -print('For the final test use chalk. this is interesting because it ' - 'uses ES6 objects like Map, Therefore we have to include the polyfills!') -chalk = js2py.require('chalk', include_polyfill=True) -chalk = chalk.constructor.new({'level': 2}) -true_text = '\x1b[34mHello \x1b[4m\x1b[44mworld\x1b[49m\x1b[24m!\x1b[39m' -text = chalk.blue('Hello', chalk.underline.bgBlue('world') + '!') -print(text) -assert text==true_text +# print('For the final test use chalk. this is interesting because it ' +# 'uses ES6 objects like Map, Therefore we have to include the polyfills!') +# chalk = js2py.require('chalk', include_polyfill=True) +# chalk = chalk.constructor.new({'level': 2}) +# true_text = '\x1b[34mHello \x1b[4m\x1b[44mworld\x1b[49m\x1b[24m!\x1b[39m' +# text = chalk.blue('Hello', chalk.underline.bgBlue('world') + '!') +# print(text) +# assert text==true_text print("Testing ECMA 6...") From 11ae438295a4e109c424e51ffbc18f78952ad4d5 Mon Sep 17 00:00:00 2001 From: Michiel van Baak Date: Tue, 11 Feb 2020 12:21:32 +0100 Subject: [PATCH 51/77] Fix print functions and a python2 only import --- js2py/internals/speed.py | 15 +++++++++++---- js2py/legecy_translators/constants.py | 3 ++- js2py/legecy_translators/exps.py | 4 +++- js2py/legecy_translators/flow.py | 4 +++- js2py/legecy_translators/functions.py | 6 ++++-- js2py/legecy_translators/jsparser.py | 11 ++++++----- js2py/legecy_translators/nodevisitor.py | 6 ++++-- js2py/legecy_translators/objects.py | 12 +++++++----- js2py/legecy_translators/translator.py | 4 +++- 9 files changed, 43 insertions(+), 22 deletions(-) diff --git a/js2py/internals/speed.py b/js2py/internals/speed.py index 3ca4079e..482eb7ea 100644 --- a/js2py/internals/speed.py +++ b/js2py/internals/speed.py @@ -1,7 +1,14 @@ +from __future__ import print_function + from timeit import timeit from collections import namedtuple from array import array -from itertools import izip +try: + #python 2 code + from itertools import izip as zip +except ImportError: + pass + from collections import deque @@ -47,7 +54,7 @@ def add(a, b): Type = None try: - print timeit( + print(timeit( """ t.append(4) @@ -56,7 +63,7 @@ def add(a, b): """, - "from __main__ import X,Y,namedtuple,array,t,add,Type, izip", - number=1000000) + "from __main__ import X,Y,namedtuple,array,t,add,Type, zip", + number=1000000)) except: raise diff --git a/js2py/legecy_translators/constants.py b/js2py/legecy_translators/constants.py index ea048f12..8ada25ad 100644 --- a/js2py/legecy_translators/constants.py +++ b/js2py/legecy_translators/constants.py @@ -1,3 +1,4 @@ +from __future__ import print_function from string import ascii_lowercase, digits ################################## StringName = u'PyJsConstantString%d_' @@ -305,4 +306,4 @@ def do_escape(source, n): ''') t, d = remove_constants(test) - print t, d + print(t, d) diff --git a/js2py/legecy_translators/exps.py b/js2py/legecy_translators/exps.py index 3c8f793c..bcd09b19 100644 --- a/js2py/legecy_translators/exps.py +++ b/js2py/legecy_translators/exps.py @@ -16,6 +16,8 @@ NOTES: Strings and other literals are not present so each = means assignment """ +from __future__ import print_function + from utils import * from jsparser import * @@ -80,4 +82,4 @@ def bass_translator(s): if __name__ == '__main__': - print bass_translator('3.ddsd = 40') + print(bass_translator('3.ddsd = 40')) diff --git a/js2py/legecy_translators/flow.py b/js2py/legecy_translators/flow.py index 9e2bb0fa..60d99aa3 100644 --- a/js2py/legecy_translators/flow.py +++ b/js2py/legecy_translators/flow.py @@ -9,6 +9,8 @@ FOR iter CONTINUE, BREAK, RETURN, LABEL, THROW, TRY, SWITCH """ +from __future__ import print_function + from utils import * from jsparser import * from nodevisitor import exp_translator @@ -477,4 +479,4 @@ def translate_flow(source): if __name__ == '__main__': #print do_dowhile('do {} while(k+f)', 0)[0] #print 'e: "%s"'%do_expression('++(c?g:h); mj', 0)[0] - print translate_flow('a; yimport test')[0] + print(translate_flow('a; yimport test')[0]) diff --git a/js2py/legecy_translators/functions.py b/js2py/legecy_translators/functions.py index 4825eee0..ceffc9e4 100644 --- a/js2py/legecy_translators/functions.py +++ b/js2py/legecy_translators/functions.py @@ -1,4 +1,6 @@ """This module removes JS functions from source code""" +from __future__ import print_function + from jsparser import * from utils import * @@ -94,5 +96,5 @@ def remove_functions(source, all_inline=False): if __name__ == '__main__': - print remove_functions( - '5+5 function n (functiona ,functionaj) {dsd s, dsdd}') + print(remove_functions( + '5+5 function n (functiona ,functionaj) {dsd s, dsdd}')) diff --git a/js2py/legecy_translators/jsparser.py b/js2py/legecy_translators/jsparser.py index a09537d3..7452b078 100644 --- a/js2py/legecy_translators/jsparser.py +++ b/js2py/legecy_translators/jsparser.py @@ -45,6 +45,7 @@ """ +from __future__ import print_function from utils import * @@ -64,7 +65,7 @@ def dbg(source): try: - with open('C:\Users\Piotrek\Desktop\dbg.py', 'w') as f: + with open(r'C:\Users\Piotrek\Desktop\dbg.py', 'w') as f: f.write(source) except: pass @@ -77,13 +78,13 @@ def indent(lines, ind=4): def inject_before_lval(source, lval, code): if source.count(lval) > 1: dbg(source) - print - print lval + print() + print(lval) raise RuntimeError('To many lvals (%s)' % lval) elif not source.count(lval): dbg(source) - print - print lval + print() + print(lval) assert lval not in source raise RuntimeError('No lval found "%s"' % lval) end = source.index(lval) diff --git a/js2py/legecy_translators/nodevisitor.py b/js2py/legecy_translators/nodevisitor.py index 4d50545c..5e20654b 100644 --- a/js2py/legecy_translators/nodevisitor.py +++ b/js2py/legecy_translators/nodevisitor.py @@ -1,3 +1,5 @@ +from __future__ import print_function + from jsparser import * from utils import * import re @@ -557,6 +559,6 @@ def exp_translator(code): #print 'Here', trans('(eee ) . ii [ PyJsMarker ] [ jkj ] ( j , j ) . # jiji (h , ji , i)(non )( )()()()') for e in xrange(3): - print exp_translator('jk = kk.ik++') + print(exp_translator('jk = kk.ik++')) #First line translated with PyJs: PyJsStrictEq(PyJsAdd((Js(100)*Js(50)),Js(30)), Js("5030")), yay! - print exp_translator('delete a.f') + print(exp_translator('delete a.f')) diff --git a/js2py/legecy_translators/objects.py b/js2py/legecy_translators/objects.py index 2abda3e8..5cb9012a 100644 --- a/js2py/legecy_translators/objects.py +++ b/js2py/legecy_translators/objects.py @@ -1,6 +1,8 @@ """ This module removes all objects/arrays from JS source code and replace them with LVALS. Also it has s function translating removed object/array to python code. Use this module just after removing constants. Later move on to removing functions""" +from __future__ import print_function + OBJECT_LVAL = 'PyJsLvalObject%d_' ARRAY_LVAL = 'PyJsLvalArray%d_' from utils import * @@ -180,7 +182,7 @@ def translate_object(obj, lval, obj_count=1, arr_count=1): try: key, value = spl except: #len(spl)> 2 - print 'Unusual case ' + repr(e) + print('Unusual case ' + repr(e)) key = spl[0] value = ':'.join(spl[1:]) key = key.strip() @@ -293,8 +295,8 @@ def translate_array(array, lval, obj_count=1, arr_count=1): #print remove_objects(test) #print list(bracket_split(' {}')) - print - print remove_arrays( + print() + print(remove_arrays( 'typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""], [][[5][5]])[1].toLowerCase()])' - ) - print is_object('', ')') + )) + print(is_object('', ')')) diff --git a/js2py/legecy_translators/translator.py b/js2py/legecy_translators/translator.py index 3573f06c..c0ad8459 100644 --- a/js2py/legecy_translators/translator.py +++ b/js2py/legecy_translators/translator.py @@ -1,3 +1,5 @@ +from __future__ import print_function + from flow import translate_flow from constants import remove_constants, recover_constants from objects import remove_objects, remove_arrays, translate_object, translate_array, set_func_translator @@ -148,4 +150,4 @@ def translate_func(name, block, args): #res = translate_js(jq) res = translate_js(t) dbg(SANDBOX % indent(res)) - print 'Done' + print('Done') From 46888c3e6ab5a594f69a25696ddb67b78097be8c Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sat, 7 Mar 2020 00:40:56 +0100 Subject: [PATCH 52/77] add support for require module versions, fix travis --- js2py/node_import.py | 27 ++++++++++++++++++++++----- simple_test.py | 4 ++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/js2py/node_import.py b/js2py/node_import.py index 605c4b3a..6acffefa 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -1,7 +1,10 @@ __all__ = ['require'] + import subprocess, os, codecs, glob from .evaljs import translate_js, DEFAULT_HEADER +from .translators.friendly_nodes import is_valid_py_name import six + DID_INIT = False DIRNAME = os.path.dirname(os.path.abspath(__file__)) PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'py_node_modules') @@ -46,15 +49,23 @@ def _init(): ''' + 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] + cand = _get_module_py_name(module_name).rpartition('/')[-1] + if not is_valid_py_name(cand): + raise ValueError( + "Invalid Python module name %s (generated from %s). Unsupported/invalid npm module specification?" % ( + repr(cand), repr(module_name))) + return cand -def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False): +def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False, maybe_version_str=""): assert isinstance(module_name, str), 'module_name must be a string!' + py_name = _get_module_py_name(module_name) module_filename = '%s.py' % py_name var_name = _get_module_var_name(module_name) @@ -74,6 +85,8 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa f.write(code.encode('utf-8') if six.PY3 else code) pkg_name = module_name.partition('/')[0] + if maybe_version_str: + pkg_name += '@' + maybe_version_str # make sure the module is installed assert subprocess.call( 'cd %s;npm install %s' % (repr(DIRNAME), pkg_name), @@ -123,7 +136,8 @@ def require(module_name, include_polyfill=False, update=False, context=None): 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 module_name: Name of the npm module to require. For example 'esprima'. Supports specific versions via @ + specification. Eg: 'crypto-js@3.3'. :param include_polyfill: Whether the babel-polyfill should be included as part of the translation. May be needed for some modules that use unsupported features. :param update: Whether to force update the translation. Otherwise uses a cached version if exists. @@ -131,7 +145,10 @@ def require(module_name, include_polyfill=False, update=False, context=None): 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) + module_name, maybe_version = (module_name+"@@@").split('@')[:2] + + py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=update, + maybe_version_str=maybe_version) # this is a bit hacky but we need to strip the default header from the generated code... if context is not None: if not py_code.startswith(DEFAULT_HEADER): @@ -141,5 +158,5 @@ def require(module_name, include_polyfill=False, update=False, context=None): 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) + exec(py_code, context) return context['var'][_get_module_var_name(module_name)].to_py() diff --git a/simple_test.py b/simple_test.py index ccb1bd98..ffc1bd9f 100644 --- a/simple_test.py +++ b/simple_test.py @@ -65,7 +65,7 @@ def get_bar(self): # crypto-js ( https://www.npmjs.com/package/crypto-js ) print('Testing crypto-js') -CryptoJS = js2py.require('crypto-js') +CryptoJS = js2py.require('crypto-js@3.1.8') data = [{'id': 1}, {'id': 2}] JSON = js2py.eval_js('JSON') ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123') @@ -73,7 +73,7 @@ def get_bar(self): decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() assert decryptedData == data -AES = js2py.require('crypto-js/aes') +AES = js2py.require('crypto-js/aes@3.1.8') ciphertext = AES.encrypt(JSON.stringify(data), 'secret key 123') bytes = AES.decrypt(ciphertext.toString(), 'secret key 123') decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() From 04241ba58f850cf586c55c25ac033561b9bb82cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Izcar=20Mu=C3=B1oz=20Torrez?= Date: Wed, 4 Mar 2020 22:57:50 -0600 Subject: [PATCH 53/77] fix error msg from python function beacuase python3+ does not support e.message , just supports str(e) and also python2+ too --- js2py/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js2py/base.py b/js2py/base.py index cf1eca08..48e62d4d 100644 --- a/js2py/base.py +++ b/js2py/base.py @@ -126,7 +126,7 @@ def PyWrapper(this, arguments, var=None): except Exception as e: message = 'your Python function failed! ' try: - message += e.message + message += str(e) except: pass raise MakeError('Error', message) @@ -1304,7 +1304,7 @@ def __call__(self, *args): except Exception as e: message = 'your Python function failed! ' try: - message += e.message + message += str(e) except: pass raise MakeError('Error', message) From 1158449646afcc21c6c394ca933d8437ed09d3df Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sat, 7 Mar 2020 14:28:59 +0100 Subject: [PATCH 54/77] release + vm fixes --- js2py/internals/constructors/jsmath.py | 15 +++++---- js2py/internals/constructors/jsstring.py | 2 +- setup.py | 2 +- tests/node_eval.py | 41 ++++++++++++++++++++++++ tests/run.py | 27 +++++++++++----- 5 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 tests/node_eval.py diff --git a/js2py/internals/constructors/jsmath.py b/js2py/internals/constructors/jsmath.py index 3eb44616..c6fde67e 100644 --- a/js2py/internals/constructors/jsmath.py +++ b/js2py/internals/constructors/jsmath.py @@ -16,7 +16,8 @@ 'SQRT1_2': 0.7071067811865476, 'SQRT2': 1.4142135623730951 } - +def is_infinity(x): + return x - 1e10 == x class MathFunctions: def abs(this, args): @@ -65,22 +66,22 @@ def atan2(this, args): def ceil(this, args): x = get_arg(args, 0) a = to_number(x) - if a != a: # it must be a nan - return NaN + if not is_finite(x): + return x return float(math.ceil(a)) def floor(this, args): x = get_arg(args, 0) a = to_number(x) - if a != a: # it must be a nan - return NaN + if not is_finite(x): + return x return float(math.floor(a)) def round(this, args): x = get_arg(args, 0) a = to_number(x) - if a != a: # it must be a nan - return NaN + if not is_finite(x): + return x return float(round(a)) def sin(this, args): diff --git a/js2py/internals/constructors/jsstring.py b/js2py/internals/constructors/jsstring.py index f2b43831..20d92a4b 100644 --- a/js2py/internals/constructors/jsstring.py +++ b/js2py/internals/constructors/jsstring.py @@ -1,6 +1,6 @@ from ..conversions import * from ..func_utils import * - +from six import unichr def fromCharCode(this, args): res = u'' diff --git a/setup.py b/setup.py index af75c2cb..aa4d3943 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.67', + version='0.68', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', diff --git a/tests/node_eval.py b/tests/node_eval.py new file mode 100644 index 00000000..48775d3a --- /dev/null +++ b/tests/node_eval.py @@ -0,0 +1,41 @@ +import subprocess +from tempfile import NamedTemporaryFile +import six +import re +import os + +class NodeJsError(Exception): + pass + +def node_eval_js(code): + ERR_MARKER = "<<>>" + ERR_REGEXP = ERR_MARKER + r'([\s\S]*?)' + ERR_MARKER + interceptor_code = """ + try { + var res = eval(%s); + console.log(res); + } catch (e) { + throw new Error(getErrMarker() + e + getErrMarker()) + } + function getErrMarker() { + return %s; + } + """ % (repr(code), repr(ERR_MARKER)) + f = NamedTemporaryFile(delete=False, suffix='.js') + f.write(interceptor_code.encode('utf-8') if six.PY3 else interceptor_code) + f.close() + p = subprocess.Popen(['node', f.name], stderr=subprocess.PIPE, stdout=subprocess.PIPE) + out, err = map(lambda x: x.decode('utf-8') if six.PY3 else x, p.communicate()) + os.unlink(f.name) + if not p.returncode: + return out + # try to get clear error message + match = re.search(ERR_REGEXP, err) + if match and len(match.groups()) == 1: + raise NodeJsError(match.groups()[0]) + else: + raise NodeJsError(err) + + +if __name__ == '__main__': + print(node_eval_js('x = 5;x')) \ No newline at end of file diff --git a/tests/run.py b/tests/run.py index 1e6f8958..7c5f32c5 100644 --- a/tests/run.py +++ b/tests/run.py @@ -5,6 +5,7 @@ from js2py.internals.simplex import JsException import os, sys, re, traceback, threading, ctypes, time, six from distutils.version import LooseVersion +from node_eval import NodeJsError, node_eval_js import codecs import json import traceback @@ -12,11 +13,12 @@ TEST_TIMEOUT = 2 INCLUDE_PATH = 'includes/' TEST_PATH = 'test_cases/language' - +FAILING = [] # choose which JS runtime to test. Js2Py has 2 independent runtimes. Translation based (translator) and the vm interpreter based. RUNTIME_TO_TEST = 'vm' + if RUNTIME_TO_TEST == 'translator': JS_EVALUATOR = js2py.eval_js PY_JS_EXCEPTION = PyJsException @@ -25,10 +27,9 @@ JS_EVALUATOR = seval.eval_js_vm PY_JS_EXCEPTION = PyJsException MESSAGE_FROM_PY_JS_EXCEPTION = lambda x: str(x) -elif RUNTIME_TO_TEST == 'execjs': - import execjs - JS_EVALUATOR = lambda x: execjs.eval('eval(%s)'% json.dumps('{;%s}' % x)) - PY_JS_EXCEPTION = execjs._exceptions.RuntimeError +elif RUNTIME_TO_TEST == 'node': + JS_EVALUATOR = node_eval_js + PY_JS_EXCEPTION = NodeJsError MESSAGE_FROM_PY_JS_EXCEPTION = lambda x: str(x) else: raise RuntimeError("Js2Py has currently only 2 runtimes available - 'translator' and the 'vm' - RUNTIME_TO_TEST must be one of these.") @@ -62,6 +63,8 @@ def terminate_thread(thread): INIT = load(os.path.join(INCLUDE_PATH, 'init.js')) +with open("node_failed.txt") as f: + NODE_FAILED = set(f.readlines()) class FestCase: description = None @@ -87,11 +90,13 @@ def __init__(self, path): if self.includes: for include in self.includes: self.init += load(os.path.join(INCLUDE_PATH, include)) - if 'onlyStrict' in self.flags: + if 'onlyStrict' in self.flags or '"use strict"' in self.raw or "'use strict'" in self.raw: self.strict_only = True else: self.strict_only = False + self.node_failed = self.es5id in NODE_FAILED + self.code = self.init + self.raw def _parse_test_info(self): @@ -184,6 +189,8 @@ def run(self): label = "PASSED" reason = '' full_error = '' + if not passed: + FAILING.append(self.es5id) self.passed, self.label, self.reason, self.full_error = passed, label, reason, full_error return passed, label, reason, full_error @@ -209,7 +216,7 @@ def list_path(path, folders=False): try: return sorted(res, key=LooseVersion) except: - return sorted(res) # python 3 + return sorted(res) # python 3... why cant they fix this def fest_all(path): files = list_path(path) @@ -221,6 +228,8 @@ def fest_all(path): test = FestCase(f) if test.strict_only: continue + if test.node_failed: + continue thread = threading.Thread(target=test.run) timeout_time = time.time() + TEST_TIMEOUT @@ -237,12 +246,14 @@ def fest_all(path): except: print(traceback.format_exc()) print(f) - raw_input() for folder in folders: fest_all(folder) fest_all(TEST_PATH) +with open('failed.txt', 'w') as f: + f.write('\n'.join(filter(lambda x: x, FAILING))) + From 55d6bf39ba75e4e5bea2ec2df9a1314148323046 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sat, 11 Apr 2020 11:36:36 +0200 Subject: [PATCH 55/77] improve undefined is not a function error message (finally) --- js2py/base.py | 15 +++++++++------ js2py/node_import.py | 4 ++-- setup.py | 2 +- simple_test.py | 3 +++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/js2py/base.py b/js2py/base.py index 48e62d4d..f4ee721c 100644 --- a/js2py/base.py +++ b/js2py/base.py @@ -319,7 +319,7 @@ def get(self, prop): #external use! #prop = prop.value if self.Class == 'Undefined' or self.Class == 'Null': raise MakeError('TypeError', - 'Undefined and null dont have properties!') + 'Undefined and null dont have properties (tried getting property %s)' % repr(prop)) if not isinstance(prop, basestring): prop = prop.to_string().value if not isinstance(prop, basestring): raise RuntimeError('Bug') @@ -361,7 +361,7 @@ def put(self, prop, val, op=None): #external use! * / % + - << >> & ^ |''' if self.Class == 'Undefined' or self.Class == 'Null': raise MakeError('TypeError', - 'Undefined and null dont have properties!') + 'Undefined and null don\'t have properties (tried setting property %s)' % repr(prop)) if not isinstance(prop, basestring): prop = prop.to_string().value if NUMPY_AVAILABLE and prop.isdigit(): @@ -991,7 +991,8 @@ def callprop(self, prop, *args): cand = self.get(prop) if not cand.is_callable(): raise MakeError('TypeError', - '%s is not a function' % cand.typeof()) + '%s is not a function (tried calling property %s of %s)' % ( + cand.typeof(), repr(prop), repr(self.Class))) return cand.call(self, args) def to_python(self): @@ -1464,9 +1465,11 @@ def call(self, this, args=()): except NotImplementedError: raise except RuntimeError as e: # maximum recursion - raise MakeError( - 'RangeError', e.message if - not isinstance(e, NotImplementedError) else 'Not implemented!') + try: + msg = e.message + except: + msg = repr(e) + raise MakeError('RangeError', msg) def has_instance(self, other): # I am not sure here so instanceof may not work lol. diff --git a/js2py/node_import.py b/js2py/node_import.py index 6acffefa..2be0985c 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -130,7 +130,7 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa return py_code -def require(module_name, include_polyfill=False, update=False, context=None): +def require(module_name, include_polyfill=True, update=False, context=None): """ Installs the provided npm module, exports a js bundle via browserify, converts to ECMA 5.1 via babel and finally translates the generated JS bundle to Python via Js2Py. @@ -139,7 +139,7 @@ def require(module_name, include_polyfill=False, update=False, context=None): :param module_name: Name of the npm module to require. For example 'esprima'. Supports specific versions via @ specification. Eg: 'crypto-js@3.3'. :param include_polyfill: Whether the babel-polyfill should be included as part of the translation. May be needed - for some modules that use unsupported features. + for some modules that use unsupported features of JS6 such as Map or typed arrays. :param update: Whether to force update the translation. Otherwise uses a cached version if exists. :param 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. diff --git a/setup.py b/setup.py index aa4d3943..ae5766e1 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.68', + version='0.70', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', diff --git a/simple_test.py b/simple_test.py index ffc1bd9f..7d34ee63 100644 --- a/simple_test.py +++ b/simple_test.py @@ -1,5 +1,8 @@ import js2py import time +import sys + +sys.setrecursionlimit(10000) print("Testing ECMA 5...") From f297498a2aef831094c3d45bdbe842967fafd7eb Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sat, 25 Apr 2020 15:02:35 +0200 Subject: [PATCH 56/77] Fix labeled block statement pretending to be a loop. --- .gitignore | 3 +++ .travis.yml | 2 ++ js2py/translators/translating_nodes.py | 11 +++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f36e4c1b..aebbc4e8 100644 --- a/.gitignore +++ b/.gitignore @@ -110,6 +110,9 @@ $RECYCLE.BIN/ .idea/scopes/ bench.py +out.py +sample.js +package-lock.json js2py/node_modules js2py/py_node_modules/* !js2py/py_node_modules/__init__.py \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 7b111cbd..79ec4174 100755 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,9 @@ python: - "3.5" - "3.6" - "3.7" + - "3.8" - "3.8-dev" + - "nightly" # command to install dependencies! install: "pip install -r requirements.txt && pip install numpy" # command to run tests! diff --git a/js2py/translators/translating_nodes.py b/js2py/translators/translating_nodes.py index 371c8ede..bfce7c1e 100644 --- a/js2py/translators/translating_nodes.py +++ b/js2py/translators/translating_nodes.py @@ -108,6 +108,13 @@ def to_key(literal_or_identifier): else: return unicode(k) +def is_iteration_statement(cand): + if not isinstance(cand, dict): + # Multiple statements. + return False + return cand.get("type", "?") in {"ForStatement", "ForInStatement", "WhileStatement", "DoWhileStatement"} + + def trans(ele, standard=False): """Translates esprima syntax tree to python by delegating to appropriate translating node""" @@ -440,8 +447,8 @@ 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'): + if is_iteration_statement(body) and (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 From e7e3d563c37d04b2ab8a33d055f661e089c4eb97 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Fri, 18 Sep 2020 10:47:18 +0200 Subject: [PATCH 57/77] Update .travis.yml Remove nightly python tag to prevent unrelated build errors (eg due to numpy not installing). --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 79ec4174..cb961fe8 100755 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,7 @@ python: - "3.7" - "3.8" - "3.8-dev" - - "nightly" # command to install dependencies! install: "pip install -r requirements.txt && pip install numpy" # command to run tests! -script: python simple_test.py \ No newline at end of file +script: python simple_test.py From d29d9d028348b4c2eb2384aa7babe5a8942b0e3a Mon Sep 17 00:00:00 2001 From: worstperson Date: Thu, 17 Sep 2020 13:01:31 -0500 Subject: [PATCH 58/77] Complete all setters! --- js2py/constructors/jsdate.py | 188 ++++++++++++++++++++++++- js2py/internals/constructors/jsdate.py | 158 ++++++++++++++++++++- 2 files changed, 340 insertions(+), 6 deletions(-) diff --git a/js2py/constructors/jsdate.py b/js2py/constructors/jsdate.py index 98de6431..9a2836a1 100644 --- a/js2py/constructors/jsdate.py +++ b/js2py/constructors/jsdate.py @@ -332,7 +332,7 @@ def setMilliseconds(ms): check_date(this) t = UTCToLocal(this.value) tim = MakeTime( - HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) + Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms) u = TimeClip(LocalToUTC(MakeDate(Day(t), tim))) this.value = u return u @@ -341,12 +341,194 @@ def setUTCMilliseconds(ms): check_date(this) t = this.value tim = MakeTime( - HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) + Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms) u = TimeClip(MakeDate(Day(t), tim)) this.value = u return u - # todo Complete all setters! + def setSeconds(sec, ms=None): + check_date(this) + t = UTCToLocal(this.value) + s = sec.to_number() + if not ms: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate( + Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli)) + u = TimeClip(LocalToUTC(date)) + this.value = u + return u + + def setMilliseconds(ms): + check_date(this) + t = UTCToLocal(this.value) + tim = MakeTime( + Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms) + u = TimeClip(LocalToUTC(MakeDate(Day(t), tim))) + this.value = u + return u + + def setUTCMilliseconds(ms): + check_date(this) + t = this.value + tim = MakeTime( + Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms) + u = TimeClip(MakeDate(Day(t), tim)) + this.value = u + return u + + def setSeconds(sec, ms=None): + check_date(this) + t = UTCToLocal(this.value) + s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate( + Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli)) + u = TimeClip(LocalToUTC(date)) + this.value = u + return u + + def setUTCSeconds(sec, ms=None): + check_date(this) + t = this.value + s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate( + Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli)) + v = TimeClip(date) + this.value = v + return v + + def setMinutes(min, sec=None, ms=None): + check_date(this) + t = UTCToLocal(this.value) + m = min.to_number() + if not sec is None: s = Js(SecFromTime(t)) + else: s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli)) + u = TimeClip(LocalToUTC(date)) + this.value = u + return u + + def setUTCMinutes(min, sec=None, ms=None): + check_date(this) + t = this.value + m = min.to_number() + if not sec is None: s = Js(SecFromTime(t)) + else: s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli)) + v = TimeClip(date) + this.value = v + return v + + def setHours(hour, min=None, sec=None, ms=None): + check_date(this) + t = UTCToLocal(this.value) + h = hour.to_number() + if not min is None: m = Js(MinFromTime(t)) + else: m = min.to_number() + if not sec is None: s = Js(SecFromTime(t)) + else: s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate(Day(t), MakeTime(h, m, s, milli)) + u = TimeClip(LocalToUTC(date)) + this.value = u + return u + + def setUTCHours(hour, min=None, sec=None, ms=None): + check_date(this) + t = this.value + h = hour.to_number() + if not min is None: m = Js(MinFromTime(t)) + else: m = min.to_number() + if not sec is None: s = Js(SecFromTime(t)) + else: s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate(Day(t), MakeTime(h, m, s, milli)) + v = TimeClip(date) + this.value = v + return v + + def setDate(date): + check_date(this) + t = UTCToLocal(this.value) + dt = date.to_number() + newDate = MakeDate( + MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t)) + u = TimeClip(LocalToUTC(newDate)) + this.value = u + return u + + def setUTCDate(date): + check_date(this) + t = this.value + dt = date.to_number() + newDate = MakeDate( + MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t)) + v = TimeClip(newDate) + this.value = v + return v + + def setMonth(month, date=None): + check_date(this) + t = UTCToLocal(this.value) + m = month.to_number() + if not date is None: dt = Js(DateFromTime(t)) + else: dt = date.to_number() + newDate = MakeDate( + MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t)) + u = TimeClip(LocalToUTC(newDate)) + this.value = u + return u + + def setUTCMonth(month, date=None): + check_date(this) + t = this.value + m = month.to_number() + if not date is None: dt = Js(DateFromTime(t)) + else: dt = date.to_number() + newDate = MakeDate( + MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t)) + v = TimeClip(newDate) + this.value = v + return v + + def setFullYear(year, month=None, date=None): + check_date(this) + if not this.value is NaN: t = UTCToLocal(this.value) + else: t = 0 + y = year.to_number() + if not month is None: m = Js(MonthFromTime(t)) + else: m = month.to_number() + if not date is None: dt = Js(DateFromTime(t)) + else: dt = date.to_number() + newDate = MakeDate( + MakeDay(y, m, dt), TimeWithinDay(t)) + u = TimeClip(LocalToUTC(newDate)) + this.value = u + return u + + def setUTCFullYear(year, month=None, date=None): + check_date(this) + if not this.value is NaN: t = UTCToLocal(this.value) + else: t = 0 + y = year.to_number() + if not month is None: m = Js(MonthFromTime(t)) + else: m = month.to_number() + if not date is None: dt = Js(DateFromTime(t)) + else: dt = date.to_number() + newDate = MakeDate( + MakeDay(y, m, dt), TimeWithinDay(t)) + v = TimeClip(newDate) + this.value = v + return v def toUTCString(): check_date(this) diff --git a/js2py/internals/constructors/jsdate.py b/js2py/internals/constructors/jsdate.py index 98de6431..5da06caf 100644 --- a/js2py/internals/constructors/jsdate.py +++ b/js2py/internals/constructors/jsdate.py @@ -332,7 +332,7 @@ def setMilliseconds(ms): check_date(this) t = UTCToLocal(this.value) tim = MakeTime( - HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) + Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms) u = TimeClip(LocalToUTC(MakeDate(Day(t), tim))) this.value = u return u @@ -341,12 +341,164 @@ def setUTCMilliseconds(ms): check_date(this) t = this.value tim = MakeTime( - HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int()) + Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms) u = TimeClip(MakeDate(Day(t), tim)) this.value = u return u - # todo Complete all setters! + def setSeconds(sec, ms=None): + check_date(this) + t = UTCToLocal(this.value) + s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate( + Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli)) + u = TimeClip(LocalToUTC(date)) + this.value = u + return u + + def setUTCSeconds(sec, ms=None): + check_date(this) + t = this.value + s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate( + Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli)) + v = TimeClip(date) + this.value = v + return v + + def setMinutes(min, sec=None, ms=None): + check_date(this) + t = UTCToLocal(this.value) + m = min.to_number() + if not sec is None: s = Js(SecFromTime(t)) + else: s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli)) + u = TimeClip(LocalToUTC(date)) + this.value = u + return u + + def setUTCMinutes(min, sec=None, ms=None): + check_date(this) + t = this.value + m = min.to_number() + if not sec is None: s = Js(SecFromTime(t)) + else: s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli)) + v = TimeClip(date) + this.value = v + return v + + def setHours(hour, min=None, sec=None, ms=None): + check_date(this) + t = UTCToLocal(this.value) + h = hour.to_number() + if not min is None: m = Js(MinFromTime(t)) + else: m = min.to_number() + if not sec is None: s = Js(SecFromTime(t)) + else: s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate(Day(t), MakeTime(h, m, s, milli)) + u = TimeClip(LocalToUTC(date)) + this.value = u + return u + + def setUTCHours(hour, min=None, sec=None, ms=None): + check_date(this) + t = this.value + h = hour.to_number() + if not min is None: m = Js(MinFromTime(t)) + else: m = min.to_number() + if not sec is None: s = Js(SecFromTime(t)) + else: s = sec.to_number() + if not ms is None: milli = Js(msFromTime(t)) + else: milli = ms.to_number() + date = MakeDate(Day(t), MakeTime(h, m, s, milli)) + v = TimeClip(date) + this.value = v + return v + + def setDate(date): + check_date(this) + t = UTCToLocal(this.value) + dt = date.to_number() + newDate = MakeDate( + MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t)) + u = TimeClip(LocalToUTC(newDate)) + this.value = u + return u + + def setUTCDate(date): + check_date(this) + t = this.value + dt = date.to_number() + newDate = MakeDate( + MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t)) + v = TimeClip(newDate) + this.value = v + return v + + def setMonth(month, date=None): + check_date(this) + t = UTCToLocal(this.value) + m = month.to_number() + if not date is None: dt = Js(DateFromTime(t)) + else: dt = date.to_number() + newDate = MakeDate( + MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t)) + u = TimeClip(LocalToUTC(newDate)) + this.value = u + return u + + def setUTCMonth(month, date=None): + check_date(this) + t = this.value + m = month.to_number() + if not date is None: dt = Js(DateFromTime(t)) + else: dt = date.to_number() + newDate = MakeDate( + MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t)) + v = TimeClip(newDate) + this.value = v + return v + + def setFullYear(year, month=None, date=None): + check_date(this) + if not this.value is NaN: t = UTCToLocal(this.value) + else: t = 0 + y = year.to_number() + if not month is None: m = Js(MonthFromTime(t)) + else: m = month.to_number() + if not date is None: dt = Js(DateFromTime(t)) + else: dt = date.to_number() + newDate = MakeDate( + MakeDay(y, m, dt), TimeWithinDay(t)) + u = TimeClip(LocalToUTC(newDate)) + this.value = u + return u + + def setUTCFullYear(year, month=None, date=None): + check_date(this) + if not this.value is NaN: t = UTCToLocal(this.value) + else: t = 0 + y = year.to_number() + if not month is None: m = Js(MonthFromTime(t)) + else: m = month.to_number() + if not date is None: dt = Js(DateFromTime(t)) + else: dt = date.to_number() + newDate = MakeDate( + MakeDay(y, m, dt), TimeWithinDay(t)) + v = TimeClip(newDate) + this.value = v + return v def toUTCString(): check_date(this) From 318d87d1a25041fdd254a69ca760382ade4c44ae Mon Sep 17 00:00:00 2001 From: worstperson Date: Thu, 17 Sep 2020 13:23:37 -0500 Subject: [PATCH 59/77] fix mistake --- js2py/constructors/jsdate.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/js2py/constructors/jsdate.py b/js2py/constructors/jsdate.py index 9a2836a1..5da06caf 100644 --- a/js2py/constructors/jsdate.py +++ b/js2py/constructors/jsdate.py @@ -346,36 +346,6 @@ def setUTCMilliseconds(ms): this.value = u return u - def setSeconds(sec, ms=None): - check_date(this) - t = UTCToLocal(this.value) - s = sec.to_number() - if not ms: milli = Js(msFromTime(t)) - else: milli = ms.to_number() - date = MakeDate( - Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli)) - u = TimeClip(LocalToUTC(date)) - this.value = u - return u - - def setMilliseconds(ms): - check_date(this) - t = UTCToLocal(this.value) - tim = MakeTime( - Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms) - u = TimeClip(LocalToUTC(MakeDate(Day(t), tim))) - this.value = u - return u - - def setUTCMilliseconds(ms): - check_date(this) - t = this.value - tim = MakeTime( - Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms) - u = TimeClip(MakeDate(Day(t), tim)) - this.value = u - return u - def setSeconds(sec, ms=None): check_date(this) t = UTCToLocal(this.value) From 483f932547485e7122c247fb274642b1fb1ea2e9 Mon Sep 17 00:00:00 2001 From: worstperson Date: Sun, 20 Sep 2020 11:25:31 -0500 Subject: [PATCH 60/77] Have Date.UTC return a number --- js2py/constructors/jsdate.py | 3 +-- js2py/internals/constructors/jsdate.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/js2py/constructors/jsdate.py b/js2py/constructors/jsdate.py index 5da06caf..674f2637 100644 --- a/js2py/constructors/jsdate.py +++ b/js2py/constructors/jsdate.py @@ -32,8 +32,7 @@ def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this mili = args[6].to_number() if l > 6 else Js(0) 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) + return TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili))) @Js diff --git a/js2py/internals/constructors/jsdate.py b/js2py/internals/constructors/jsdate.py index 5da06caf..674f2637 100644 --- a/js2py/internals/constructors/jsdate.py +++ b/js2py/internals/constructors/jsdate.py @@ -32,8 +32,7 @@ def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this mili = args[6].to_number() if l > 6 else Js(0) 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) + return TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili))) @Js From 92250a4a963c841f5834e42a4699f19530273898 Mon Sep 17 00:00:00 2001 From: worstperson Date: Sun, 20 Sep 2020 11:31:21 -0500 Subject: [PATCH 61/77] Workaround limits of datetime.datetime.utcfromtimestamp Makes it possible to load timestamps starting from Jan 01 1900 rather than Jan 01 1970 console.log(new Date(70, 0, 1, 0, 0, 0)); console.log(new Date(Date.UTC(70, 0, 1, 6, 0, 0))); console.log(new Date(0, 0, 1, 0, 0, 0)); console.log(new Date(Date.UTC(0, 0, 1, 6, 0, 0))); --- js2py/constructors/jsdate.py | 7 ++++--- js2py/internals/constructors/jsdate.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/js2py/constructors/jsdate.py b/js2py/constructors/jsdate.py index 674f2637..5aed830e 100644 --- a/js2py/constructors/jsdate.py +++ b/js2py/constructors/jsdate.py @@ -75,11 +75,12 @@ def __init__(self, value, prototype=None): # todo fix this problematic datetime part def to_local_dt(self): - return datetime.datetime.utcfromtimestamp( - UTCToLocal(self.value) // 1000) + return datetime.datetime(1970, 1, 1) + datetime.timedelta( + seconds=UTCToLocal(self.value) // 1000) def to_utc_dt(self): - return datetime.datetime.utcfromtimestamp(self.value // 1000) + return datetime.datetime(1970, 1, 1) + datetime.timedelta( + seconds=self.value // 1000) def local_strftime(self, pattern): if self.value is NaN: diff --git a/js2py/internals/constructors/jsdate.py b/js2py/internals/constructors/jsdate.py index 674f2637..5aed830e 100644 --- a/js2py/internals/constructors/jsdate.py +++ b/js2py/internals/constructors/jsdate.py @@ -75,11 +75,12 @@ def __init__(self, value, prototype=None): # todo fix this problematic datetime part def to_local_dt(self): - return datetime.datetime.utcfromtimestamp( - UTCToLocal(self.value) // 1000) + return datetime.datetime(1970, 1, 1) + datetime.timedelta( + seconds=UTCToLocal(self.value) // 1000) def to_utc_dt(self): - return datetime.datetime.utcfromtimestamp(self.value // 1000) + return datetime.datetime(1970, 1, 1) + datetime.timedelta( + seconds=self.value // 1000) def local_strftime(self, pattern): if self.value is NaN: From a7dac6bd1fc091cdfd4d80615707efc4929b2529 Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sat, 26 Sep 2020 00:00:04 +0200 Subject: [PATCH 62/77] Remove idiotic codeclimate --- .codeclimate.yml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .codeclimate.yml diff --git a/.codeclimate.yml b/.codeclimate.yml deleted file mode 100644 index 225344ab..00000000 --- a/.codeclimate.yml +++ /dev/null @@ -1,2 +0,0 @@ -exclude_paths: -- "js2py/es6/babel.py" From 042bf233f0e7ec460e98885e0cc11a3c76ce008d Mon Sep 17 00:00:00 2001 From: worstperson Date: Thu, 24 Sep 2020 19:07:34 -0500 Subject: [PATCH 63/77] [WIN] Fix DaylightSavingTA This was found in issue 215 - loading ssf.js in Windows will result in an exception in DaylightSavingTA causing it to return a fixed value and display this message: Warning: Invalid datetime date, assumed DST time, may be inaccurate... Here is the trace from Python 3.8.5: Traceback (most recent call last): File "test.py", line 28, in context.execute(js) File "C:\Python38\Js2Py-master\js2py\evaljs.py", line 199, in execute exec (compiled, self._context) File "", line 2174, in File "C:\Python38\Js2Py-master\js2py\base.py", line 949, in __call__ return self.call(self.GlobalObject, args) File "C:\Python38\Js2Py-master\js2py\base.py", line 1464, in call return Js(self.code(*args)) File "", line 1541, in PyJs_make_ssf_0_ File "C:\Python38\Js2Py-master\js2py\constructors\jsdate.py", line 149, in date_constructor return date_constructor2(*args) File "C:\Python38\Js2Py-master\js2py\constructors\jsdate.py", line 181, in date_constructor2 LocalToUTC(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))) File "C:\Python38\Js2Py-master\js2py\constructors\time_helpers.py", line 54, in LocalToUTC return t - LocalTZA - DaylightSavingTA(t - LocalTZA) File "C:\Python38\Js2Py-master\js2py\constructors\time_helpers.py", line 39, in DaylightSavingTA LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp( OSError: [Errno 22] Invalid argument --- js2py/constructors/time_helpers.py | 4 ++-- js2py/internals/constructors/time_helpers.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js2py/constructors/time_helpers.py b/js2py/constructors/time_helpers.py index a744d8ca..f3d21f3e 100644 --- a/js2py/constructors/time_helpers.py +++ b/js2py/constructors/time_helpers.py @@ -36,8 +36,8 @@ def DaylightSavingTA(t): return t try: return int( - LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp( - t // 1000)).seconds) * 1000 + LOCAL_ZONE.dst(datetime.datetime(1970, 1, 1) + datetime.timedelta( + seconds=t // 1000)).seconds) * 1000 except: warnings.warn( 'Invalid datetime date, assumed DST time, may be inaccurate...', diff --git a/js2py/internals/constructors/time_helpers.py b/js2py/internals/constructors/time_helpers.py index eda95fb6..2d694b58 100644 --- a/js2py/internals/constructors/time_helpers.py +++ b/js2py/internals/constructors/time_helpers.py @@ -38,8 +38,8 @@ def DaylightSavingTA(t): return t try: return int( - LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp( - t // 1000)).seconds) * 1000 + LOCAL_ZONE.dst(datetime.datetime(1970, 1, 1) + datetime.timedelta( + seconds=t // 1000)).seconds) * 1000 except: warnings.warn( 'Invalid datetime date, assumed DST time, may be inaccurate...', From 9d89657b8cb22ad8a64d5a514396ffb5ea75649d Mon Sep 17 00:00:00 2001 From: worstperson Date: Thu, 24 Sep 2020 19:25:55 -0500 Subject: [PATCH 64/77] [WIN] Fix mirrored code in internals/base.py --- js2py/internals/base.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js2py/internals/base.py b/js2py/internals/base.py index a02a2122..938dec37 100644 --- a/js2py/internals/base.py +++ b/js2py/internals/base.py @@ -602,11 +602,12 @@ def __init__(self, value, prototype=None): # todo fix this problematic datetime part def to_local_dt(self): - return datetime.datetime.utcfromtimestamp( - self.UTCToLocal(self.value) // 1000) + return datetime.datetime(1970, 1, 1) + datetime.timedelta( + seconds=self.UTCToLocal(self.value) // 1000) def to_utc_dt(self): - return datetime.datetime.utcfromtimestamp(self.value // 1000) + return datetime.datetime(1970, 1, 1) + datetime.timedelta( + seconds=self.value // 1000) def local_strftime(self, pattern): if self.value is NaN: From e89cb94a57a7162158f68e068e25c1300473e401 Mon Sep 17 00:00:00 2001 From: ralphwetzel Date: Sun, 3 Jan 2021 15:29:16 +0100 Subject: [PATCH 65/77] Adding property context to EvalJs --- js2py/evaljs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/js2py/evaljs.py b/js2py/evaljs.py index ef4d7d95..57f18763 100644 --- a/js2py/evaljs.py +++ b/js2py/evaljs.py @@ -238,6 +238,10 @@ def eval_debug(self, expression): self.execute_debug(code) return self['PyJsEvalResult'] + @property + def context(self): + return self._context + def __getattr__(self, var): return getattr(self._var, var) From f1e5b9c50d95d4c68d5bd75c67f377f2419ec15c Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Tue, 16 Feb 2021 13:49:41 -0500 Subject: [PATCH 66/77] Adds basic support for JS input date formats --- js2py/constructors/jsdate.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/js2py/constructors/jsdate.py b/js2py/constructors/jsdate.py index 5aed830e..2e7c460f 100644 --- a/js2py/constructors/jsdate.py +++ b/js2py/constructors/jsdate.py @@ -118,21 +118,29 @@ def utc_strftime(self, pattern): def parse_date(py_string): # todo support all date string formats - try: + supported_formats = ( + "%Y-%m-%dT%H:%M:%S.%fZ", + "%Y-%m-%dT%H:%M:%SZ", + "%Y-%m-%d", + "%m/%d/%Y", + "%b %d %Y", + ) + for date_format in supported_formats: 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) + dt = datetime.datetime.strptime(py_string, date_format) + except ValueError: + continue + else: + 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))) + + raise MakeError( + 'TypeError', + 'Could not parse date %s - unsupported date format. Currently only supported formats are RFC3339 utc, ISO Date, Short Date, and Long Date. Sorry!' + % py_string) def date_constructor(*args): From ea16b519a0f72e17416859a57890b8388fce6e39 Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Thu, 18 Feb 2021 14:11:36 -0500 Subject: [PATCH 67/77] Supports datetimes withouth Z indicator -> Refactored a little bit to avoid repetition -> Leaving the explicit + ('',) because I think it shows the intent better --- js2py/constructors/jsdate.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/js2py/constructors/jsdate.py b/js2py/constructors/jsdate.py index 2e7c460f..b6b70a81 100644 --- a/js2py/constructors/jsdate.py +++ b/js2py/constructors/jsdate.py @@ -118,13 +118,24 @@ def utc_strftime(self, pattern): def parse_date(py_string): # todo support all date string formats - supported_formats = ( - "%Y-%m-%dT%H:%M:%S.%fZ", - "%Y-%m-%dT%H:%M:%SZ", + date_formats = ( "%Y-%m-%d", "%m/%d/%Y", "%b %d %Y", ) + # Supports these hour formats and with or hour. + hour_formats = ( + "T%H:%M:%S.%f", + "T%H:%M:%S", + ) + ('',) + # Supports with or without Z indicator. + z_formats = ("Z",) + ('',) + supported_formats = [ + d + t + z + for d in date_formats + for t in hour_formats + for z in z_formats + ] for date_format in supported_formats: try: dt = datetime.datetime.strptime(py_string, date_format) From 5f665f60083a9796ec33861240ce31d6d2b844b6 Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Thu, 1 Apr 2021 20:24:19 +0200 Subject: [PATCH 68/77] install the npm libraries in the tmp folder --- .gitignore | 1 + js2py/evaljs.py | 11 ----------- js2py/internals/__init__.py | 1 + js2py/node_import.py | 15 ++++++++++----- js2py/translators/translator.py | 13 ++++++++----- setup.py | 2 +- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index aebbc4e8..ab5e9100 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,7 @@ Icon # Files that might appear on external disk .Spotlight-V100 .Trashes +venv # Directories potentially created on remote AFP share .AppleDB diff --git a/js2py/evaljs.py b/js2py/evaljs.py index 57f18763..8ed4ea5c 100644 --- a/js2py/evaljs.py +++ b/js2py/evaljs.py @@ -272,14 +272,3 @@ def console(self): 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() diff --git a/js2py/internals/__init__.py b/js2py/internals/__init__.py index e69de29b..5473e711 100644 --- a/js2py/internals/__init__.py +++ b/js2py/internals/__init__.py @@ -0,0 +1 @@ +from .seval import eval_js_vm \ No newline at end of file diff --git a/js2py/node_import.py b/js2py/node_import.py index 2be0985c..21844314 100644 --- a/js2py/node_import.py +++ b/js2py/node_import.py @@ -4,10 +4,13 @@ from .evaljs import translate_js, DEFAULT_HEADER from .translators.friendly_nodes import is_valid_py_name import six +import tempfile +import hashlib +import random DID_INIT = False -DIRNAME = os.path.dirname(os.path.abspath(__file__)) -PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'py_node_modules') +DIRNAME = tempfile.mkdtemp() +PY_NODE_MODULES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'py_node_modules') def _init(): @@ -72,8 +75,10 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa 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' + module_hash = hashlib.sha1(module_name.encode("utf-8")).hexdigest()[:15] + version = random.randrange(10000000000000) + in_file_name = 'in_%s_%d.js' % (module_hash, version) + out_file_name = 'out_%s_%d.js' % (module_hash, version) code = ADD_TO_GLOBALS_FUNC if include_polyfill: code += "\n;require('babel-polyfill');\n" @@ -106,7 +111,7 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa with codecs.open(os.path.join(DIRNAME, out_file_name), "r", "utf-8") as f: js_code = f.read() - os.remove(os.path.join(DIRNAME, out_file_name)) + print("Bundled JS library dumped at: %s" % os.path.join(DIRNAME, out_file_name)) if len(js_code) < 50: raise RuntimeError("Candidate JS bundle too short - likely browserify issue.") js_code += GET_FROM_GLOBALS_FUNC diff --git a/js2py/translators/translator.py b/js2py/translators/translator.py index 16ed5bdb..5ec47002 100644 --- a/js2py/translators/translator.py +++ b/js2py/translators/translator.py @@ -55,16 +55,19 @@ def dbg(x): """does nothing, legacy dummy function""" return '' +# Another way of doing that would be with my auto esprima translation but its much slower: +# parsed = esprima.parse(js).to_dict() +def pyjsparser_parse_fn(code): + parser = pyjsparser.PyJsParser() + return parser.parse(code) -def translate_js(js, HEADER=DEFAULT_HEADER, use_compilation_plan=False): +def translate_js(js, HEADER=DEFAULT_HEADER, use_compilation_plan=False, parse_fn=pyjsparser_parse_fn): """js has to be a javascript source code. returns equivalent python code.""" if use_compilation_plan and not '//' in js and not '/*' in js: return translate_js_with_compilation_plan(js, HEADER=HEADER) - parser = pyjsparser.PyJsParser() - parsed = parser.parse(js) # js to esprima syntax tree - # Another way of doing that would be with my auto esprima translation but its much slower and causes import problems: - # parsed = esprima.parse(js).to_dict() + + parsed = parse_fn(js) translating_nodes.clean_stacks() return HEADER + translating_nodes.trans( parsed) # syntax tree to python code diff --git a/setup.py b/setup.py index ae5766e1..d195a173 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.70', + version='0.71', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', From 82ad65c6d95e2514491c9a29763627e98af04686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Bar=C4=87?= Date: Tue, 22 Jun 2021 21:11:39 +0200 Subject: [PATCH 69/77] setup.cfg: description-file -> description_file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit future releases of setuptools will not support "description-file" '''/usr/lib/python3.9/site-packages/setuptools/dist.py:642: UserWarning: Usage of dash-separated 'description-file' will not be supported in future versions. Please use the underscore name 'description_file' instead''' warning discovered on setuptools, version 56.0.0 Signed-off-by: Maciej Barć --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 5707cb06..101fbf34 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -description-file = README.md +description_file = README.md [yapf] based_on_style = pep8 \ No newline at end of file From 7ec61516ad170bd2884bbd93a69ffa6c07da9af5 Mon Sep 17 00:00:00 2001 From: GasinAn Date: Sat, 11 Jan 2020 08:23:29 +0800 Subject: [PATCH 70/77] Found BUG of translate_file and fixed Function translate_file uses function write_file_contents. However, function write_file_contents doesn't write Python code while using UTF-8 to encode. It will cause problems after translate JS file with special strings ( e.g., Chinese) to Python file. Error will be raised when importing objects from the Python file. Just like function get_file_contents, used codecs.open to fix the problem --- js2py/evaljs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js2py/evaljs.py b/js2py/evaljs.py index 8ed4ea5c..f4649c4d 100644 --- a/js2py/evaljs.py +++ b/js2py/evaljs.py @@ -53,7 +53,7 @@ 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: + with codecs.open(path_as_local(path_or_file), "w", "utf-8") as f: f.write(contents) From 1980384d6d8d040b2617a6ace077804936623d05 Mon Sep 17 00:00:00 2001 From: bjones1 Date: Sun, 12 Sep 2021 23:05:00 +0100 Subject: [PATCH 71/77] fix String.replace(regex, '&$'). These tests now pass on both the translator and the VM: - String/prototype/replace 15.5.4.11_A2_T3 - String/prototype/replace 15.5.4.11_A2_T8 See https://262.ecma-international.org/5.1/#sec-15.5.4.11 for details on this replacement string value. --- js2py/internals/prototypes/jsstring.py | 5 +++++ js2py/prototypes/jsstring.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/js2py/internals/prototypes/jsstring.py b/js2py/internals/prototypes/jsstring.py index be38802e..e8473af8 100644 --- a/js2py/internals/prototypes/jsstring.py +++ b/js2py/internals/prototypes/jsstring.py @@ -22,6 +22,11 @@ def replacement_template(rep, source, span, npar): res += '$' n += 2 continue + elif rep[n + 1] == '&': + # replace with matched string + res += source[span[0]:span[1]] + n += 2 + continue elif rep[n + 1] == '`': # replace with string that is BEFORE match res += source[:span[0]] diff --git a/js2py/prototypes/jsstring.py b/js2py/prototypes/jsstring.py index ee320709..a313bfb9 100644 --- a/js2py/prototypes/jsstring.py +++ b/js2py/prototypes/jsstring.py @@ -17,6 +17,11 @@ def replacement_template(rep, source, span, npar): res += '$' n += 2 continue + elif rep[n + 1] == '&': + # replace with matched string + res += source[span[0]:span[1]] + n += 2 + continue elif rep[n + 1] == '`': # replace with string that is BEFORE match res += source[:span[0]] From d02ebf2c69c2967c5099f385a3d35817bcb6a35e Mon Sep 17 00:00:00 2001 From: bjones1 Date: Sun, 12 Sep 2021 23:05:41 +0100 Subject: [PATCH 72/77] Use threading.is_alive instead of older threading.isAlive name. --- tests/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run.py b/tests/run.py index 7c5f32c5..1916dfa4 100644 --- a/tests/run.py +++ b/tests/run.py @@ -45,7 +45,7 @@ def terminate_thread(thread): :param thread: a threading.Thread instance """ - if not thread.isAlive(): + if not thread.is_alive(): return exc = ctypes.py_object(SystemExit) From 630da5a564ec691a5ee82403a13c5b5c669ed558 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Mon, 16 Aug 2021 01:55:08 +0300 Subject: [PATCH 73/77] Add github actions workflow --- .github/workflows/tests.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..53a13871 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,35 @@ +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', 'pypy-3.7' ] + exclude: + - os: windows-latest + python-version: '3.5' + - os: macos-latest + python-version: 'pypy-3.6' + - os: macos-latest + python-version: 'pypy-3.7' + 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 From 19cce6a062788cc3da0e89fe3ff1c01b7a2ef61c Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Thu, 16 Sep 2021 13:17:05 +0200 Subject: [PATCH 74/77] Fix for loop update after break --- js2py/host/console.py | 2 +- js2py/translators/translating_nodes.py | 77 ++++++++++++++++++-------- simple_test.py | 26 +++++++++ 3 files changed, 80 insertions(+), 25 deletions(-) diff --git a/js2py/host/console.py b/js2py/host/console.py index 50a08632..ba36b574 100644 --- a/js2py/host/console.py +++ b/js2py/host/console.py @@ -6,7 +6,7 @@ def console(): @Js def log(): - print(arguments[0]) + print(*arguments.to_list()) console.put('log', log) console.put('debug', log) diff --git a/js2py/translators/translating_nodes.py b/js2py/translators/translating_nodes.py index bfce7c1e..4e2b5760 100644 --- a/js2py/translators/translating_nodes.py +++ b/js2py/translators/translating_nodes.py @@ -14,26 +14,36 @@ LINE_LEN_LIMIT = 400 # 200 # or any other value - the larger the smaller probability of errors :) -class ForController: +class LoopController: def __init__(self): - self.inside = [False] - self.update = '' + self.update = [""] + self.label_to_update_idx = {} - def enter_for(self, update): - self.inside.append(True) - self.update = update + def enter(self, update=""): + self.update.append(update) - def leave_for(self): - self.inside.pop() + def leave(self): + self.update.pop() + + def get_update(self, label=None): + if label is None: + return self.update[-1] + if label not in self.label_to_update_idx: + raise SyntaxError("Undefined label %s" % label) + if self.label_to_update_idx[label] >= len(self.update): + raise SyntaxError("%s is not a iteration statement label?" % label) + return self.update[self.label_to_update_idx[label]] + + def register_label(self, label): + if label in self.label_to_update_idx: + raise SyntaxError("label %s already used") + self.label_to_update_idx[label] = len(self.update) + + def deregister_label(self, label): + del self.label_to_update_idx[label] - def enter_other(self): - self.inside.append(False) - def leave_other(self): - self.inside.pop() - def is_inside(self): - return self.inside[-1] class InlineStack: @@ -86,9 +96,10 @@ def get_code(self): def clean_stacks(): - global Context, inline_stack + global Context, inline_stack, loop_controller Context = ContextStack() inline_stack = InlineStack() + loop_controller = LoopController() def to_key(literal_or_identifier): @@ -374,9 +385,14 @@ def BreakStatement(type, label): def ContinueStatement(type, label): if label: - return 'raise %s("Continued")\n' % (get_continue_label(label['name'])) + maybe_update_expr = loop_controller.get_update(label=label['name']) + continue_stmt = 'raise %s("Continued")\n' % (get_continue_label(label['name'])) else: - return 'continue\n' + maybe_update_expr = loop_controller.get_update() + continue_stmt = "continue\n" + if maybe_update_expr: + return "# continue update\n%s\n%s" % (maybe_update_expr, continue_stmt) + return continue_stmt def ReturnStatement(type, argument): @@ -393,24 +409,28 @@ def DebuggerStatement(type): def DoWhileStatement(type, body, test): - inside = trans(body) + 'if not %s:\n' % trans(test) + indent('break\n') + loop_controller.enter() + body_code = trans(body) + loop_controller.leave() + inside = body_code + 'if not %s:\n' % trans(test) + indent('break\n') result = 'while 1:\n' + indent(inside) return result def ForStatement(type, init, test, update, body): - update = indent(trans(update)) if update else '' + update = trans(update) if update else '' init = trans(init) if init else '' if not init.endswith('\n'): init += '\n' test = trans(test) if test else '1' + loop_controller.enter(update) 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) + result += indent("%s# update\n%s\n" % (trans(body), update)) + loop_controller.leave() return result @@ -429,7 +449,9 @@ def ForInStatement(type, left, right, body, each): name = left['name'] else: raise RuntimeError('Unusual ForIn loop') + loop_controller.enter() res += indent('var.put(%s, PyJsTemp)\n' % repr(name) + trans(body)) + loop_controller.leave() return res @@ -445,20 +467,23 @@ def IfStatement(type, test, consequent, alternate): def LabeledStatement(type, label, body): # todo consider using smarter approach! + label_name = label['name'] + loop_controller.register_label(label_name) inside = trans(body) + loop_controller.deregister_label(label_name) defs = '' if is_iteration_statement(body) and (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']) + 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']) + 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 @@ -553,7 +578,11 @@ def VariableDeclaration(type, declarations, kind): def WhileStatement(type, test, body): - result = 'while %s:\n' % trans(test) + indent(trans(body)) + test_code = trans(test) + loop_controller.enter() + body_code = trans(body) + loop_controller.leave() + result = 'while %s:\n' % test_code + indent(body_code) return result diff --git a/simple_test.py b/simple_test.py index 7d34ee63..35fc5a18 100644 --- a/simple_test.py +++ b/simple_test.py @@ -6,6 +6,32 @@ print("Testing ECMA 5...") +continue_labels_test = """ +for (i=0;i<10;i++) { +} +a:for (i=0;i<10;i++) { + b:for (j=0;j<10;j++) { + continue a; + } +} +if (i !== 10 || j !== 0) { + throw Error("i: " + i + " j: " + j) +} +""" +print(js2py.translate_js(continue_labels_test)) +js2py.eval_js(continue_labels_test) + +break_labels_test = """ +a:for (i=0;i<10;i++) { + b:for (j=0;j<10;j++) { + break a; + } +} +if (i !== 0 || j !== 0) { + throw Error("i: " + i + " j: " + j) +} +""" +js2py.eval_js(break_labels_test) assert js2py.eval_js('(new Date("2008-9-03T20:56:35.450686Z")).toString()') From b16d7ce90ac9c03358010c1599c3e87698c9993f Mon Sep 17 00:00:00 2001 From: PiotrDabkowski Date: Sat, 18 Sep 2021 15:29:34 +0200 Subject: [PATCH 75/77] fix py2 print --- js2py/host/console.py | 2 +- simple_test.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/js2py/host/console.py b/js2py/host/console.py index ba36b574..269eef3d 100644 --- a/js2py/host/console.py +++ b/js2py/host/console.py @@ -6,7 +6,7 @@ def console(): @Js def log(): - print(*arguments.to_list()) + print(" ".join(repr(element) for element in arguments.to_list())) console.put('log', log) console.put('debug', log) diff --git a/simple_test.py b/simple_test.py index 35fc5a18..9b6918f2 100644 --- a/simple_test.py +++ b/simple_test.py @@ -18,7 +18,6 @@ throw Error("i: " + i + " j: " + j) } """ -print(js2py.translate_js(continue_labels_test)) js2py.eval_js(continue_labels_test) break_labels_test = """ From 70fe4f557e957db082ee49f8e9b4cf4ac80071ef Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sun, 6 Nov 2022 10:32:44 +0100 Subject: [PATCH 76/77] support python 3.11 bytecode, fixes #282 --- bench.dockerfile | 13 +++ js2py/internals/opcodes.py | 2 +- js2py/utils/injector.py | 171 ++++++++++++++++++++++++++++++------- setup.py | 2 +- 4 files changed, 155 insertions(+), 33 deletions(-) create mode 100644 bench.dockerfile diff --git a/bench.dockerfile b/bench.dockerfile new file mode 100644 index 00000000..a443348c --- /dev/null +++ b/bench.dockerfile @@ -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"] \ No newline at end of file diff --git a/js2py/internals/opcodes.py b/js2py/internals/opcodes.py index 15c57ccd..2f6c65f5 100644 --- a/js2py/internals/opcodes.py +++ b/js2py/internals/opcodes.py @@ -798,7 +798,7 @@ def eval(self, ctx): g = '' for g in globals(): try: - if not issubclass(globals()[g], OP_CODE) or g is 'OP_CODE': + if not issubclass(globals()[g], OP_CODE) or g == 'OP_CODE': continue except: continue diff --git a/js2py/utils/injector.py b/js2py/utils/injector.py index ea236d5e..48fe145e 100644 --- a/js2py/utils/injector.py +++ b/js2py/utils/injector.py @@ -26,17 +26,19 @@ def fix_js_args(func): return func code = append_arguments(six.get_function_code(func), ('this', 'arguments')) - return types.FunctionType( + result = types.FunctionType( code, six.get_function_globals(func), func.__name__, closure=six.get_function_closure(func)) + return result 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) + new_args = tuple(e for e in new_locals if e not in co_names) + co_names += new_args co_argcount = code_obj.co_argcount # Argument count co_code = code_obj.co_code # The actual bytecode as a string @@ -76,26 +78,51 @@ def append_arguments(code_obj, new_locals): names_to_varnames = dict( (co_names.index(name), varnames.index(name)) for name in new_locals) + is_new_bytecode = sys.version_info >= (3, 11) # Now we modify the actual bytecode modified = [] + drop_future_cache = False for inst in instructions(code_obj): + if inst.opname == "CACHE": + assert inst.arg == 0 + if not drop_future_cache: + modified.extend(write_instruction(inst.opcode, inst.arg)) + else: + # We need to inject NOOP to not break jumps :( + modified.extend(write_instruction(dis.opmap["NOP"], 0)) + + continue op, arg = inst.opcode, inst.arg # 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. + drop_future_cache = False if inst.opcode == LOAD_GLOBAL: - if inst.arg in names_to_varnames: + idx = inst.arg + if is_new_bytecode: + idx = idx // 2 + if idx in names_to_varnames: op = LOAD_FAST - arg = names_to_varnames[inst.arg] - elif inst.arg in name_translations: - arg = name_translations[inst.arg] + arg = names_to_varnames[idx] + # Cache is not present after LOAD_FAST and needs to be removed. + drop_future_cache = True + elif idx in name_translations: + tgt = name_translations[idx] + if is_new_bytecode: + tgt = 2*tgt + (inst.arg % 2) + arg = tgt else: - raise ValueError("a name was lost in translation") + raise(ValueError("a name was lost in translation last instruction %s" % str(inst))) # If it accesses co_varnames or co_names then update its argument. elif inst.opcode in opcode.haslocal: arg = varname_translations[inst.arg] elif inst.opcode in opcode.hasname: + # for example STORE_GLOBAL arg = name_translations[inst.arg] + elif is_new_bytecode and inst.opcode in opcode.hasfree: + # Python 3.11+ adds refs at the end (after locals), for whatever reason... + if inst.argval not in code_obj.co_varnames[:code_obj.co_argcount]: # we do not need to remap existing arguments, they are not shifted by new ones. + arg = inst.arg + len(new_locals) modified.extend(write_instruction(op, arg)) if six.PY2: code = ''.join(modified) @@ -113,23 +140,26 @@ def append_arguments(code_obj, new_locals): code_obj.co_filename, code_obj.co_name, code_obj.co_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( + code_obj = 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) + return code_obj else: return types.CodeType(*args) -def instructions(code_obj): - # easy for python 3.4+ - if sys.version_info >= (3, 4): +def instructions(code_obj, show_cache=True): + if sys.version_info >= (3, 11): + # Python 3.11 introduced "cache instructions", hidden by default. + for inst in dis.Bytecode(code_obj, show_caches=show_cache): + yield inst + elif sys.version_info >= (3, 4): # easy for python 3.4+ for inst in dis.Bytecode(code_obj): yield inst else: @@ -171,7 +201,7 @@ def write_instruction(op, arg): chr((arg >> 8) & 255) ] else: - raise ValueError("Invalid oparg: {0} is too large".format(oparg)) + raise ValueError("Invalid oparg: {0} is too large".format(arg)) else: # python 3.6+ uses wordcode instead of bytecode and they already supply all the EXTENDEND_ARG ops :) if arg is None: return [chr(op), 0] @@ -191,6 +221,7 @@ def write_instruction(op, arg): # 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)) @@ -221,24 +252,102 @@ def check(code_obj): 'Your python version made changes to the bytecode') +def pi(ins): + return(f"{ins.opname} {ins.arg} {ins.argval}") + + + +def signature(func): + code_obj = six.get_function_code(func) + return (code_obj.co_nlocals, code_obj.co_argcount, code_obj.co_nlocals, code_obj.co_stacksize, + code_obj.co_flags, code_obj.co_names, code_obj.co_varnames, + code_obj.co_filename, + code_obj.co_freevars, code_obj.co_cellvars) + check(six.get_function_code(check)) + + +def compare_func(fake_func, gt_func): + print(signature(fake_func)) + print(signature(gt_func)) + assert signature(fake_func) == signature(gt_func) + fake_ins = list(instructions(six.get_function_code(fake_func), show_cache=False)) + real_ins = list(instructions(six.get_function_code(gt_func), show_cache=False)) + offset = 0 + pos = 0 + for e in fake_ins: + if e.opname == "NOP": + offset += 1 # ignore NOPs that are inserted in place of old cache. + else: + real = real_ins[pos] + fake = e + print(f"POS {pos} OFFSET: {offset} FAKE VS REAL") + print(fake) + print(real) + assert fake.opcode == real.opcode + if fake.opcode in dis.hasjabs or fake.opcode in dis.hasjrel: + pass + else: + assert fake.arg == real.arg + assert fake.argval == real.argval or fake.opname in ["LOAD_CONST"] + assert fake.is_jump_target == real.is_jump_target + + pos += 1 + assert pos == len(real_ins), (pos, len(real_ins)) + print("DONE, looks good.") + + if __name__ == '__main__': - 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 + import faulthandler + + faulthandler.enable() + + def func(cmpfn): + if not this.Class in ('Array', 'Arguments'): + return this.to_object() # do nothing + arr = [] + for i in xrange(len(this)): + arr.append(this.get(six.text_type(i))) + + if not arr: + return this + if not cmpfn.is_callable(): + cmpfn = None + cmp = lambda a, b: sort_compare(a, b, cmpfn) + if six.PY3: + key = functools.cmp_to_key(cmp) + arr.sort(key=key) + else: + arr.sort(cmp=cmp) + for i in xrange(len(arr)): + this.put(six.text_type(i), arr[i]) + + return this + + + def func_gt(cmpfn, this, arguments): + if not this.Class in ('Array', 'Arguments'): + return this.to_object() # do nothing + arr = [] + for i in xrange(len(this)): + arr.append(this.get(six.text_type(i))) + + if not arr: + return this + if not cmpfn.is_callable(): + cmpfn = None + cmp = lambda a, b: sort_compare(a, b, cmpfn) + if six.PY3: + key = functools.cmp_to_key(cmp) + arr.sort(key=key) + else: + arr.sort(cmp=cmp) + for i in xrange(len(arr)): + this.put(six.text_type(i), arr[i]) + + return this + + + func2 = fix_js_args(func) + compare_func(func2, func_gt) diff --git a/setup.py b/setup.py index d195a173..7cb39fff 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.71', + version='0.72', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals', From 2e017b86e2f18a6c8a842293b1687f2ce7baa12e Mon Sep 17 00:00:00 2001 From: Piotr Dabkowski Date: Sun, 6 Nov 2022 10:56:37 +0100 Subject: [PATCH 77/77] remove template and opname not supported in old python version --- .github/workflows/tests.yml | 4 +--- js2py/utils/injector.py | 7 ++----- setup.py | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 53a13871..0656399b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,14 +11,12 @@ jobs: # 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', 'pypy-3.7' ] + 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' - - os: macos-latest - python-version: 'pypy-3.7' name: ${{ matrix.os }} ${{ matrix.arch }}, Python ${{ matrix.python-version }} steps: - uses: actions/checkout@v2 diff --git a/js2py/utils/injector.py b/js2py/utils/injector.py index 48fe145e..88e0d93e 100644 --- a/js2py/utils/injector.py +++ b/js2py/utils/injector.py @@ -83,7 +83,7 @@ def append_arguments(code_obj, new_locals): modified = [] drop_future_cache = False for inst in instructions(code_obj): - if inst.opname == "CACHE": + if is_new_bytecode and inst.opname == "CACHE": assert inst.arg == 0 if not drop_future_cache: modified.extend(write_instruction(inst.opcode, inst.arg)) @@ -252,9 +252,6 @@ def check(code_obj): 'Your python version made changes to the bytecode') -def pi(ins): - return(f"{ins.opname} {ins.arg} {ins.argval}") - def signature(func): @@ -282,7 +279,7 @@ def compare_func(fake_func, gt_func): else: real = real_ins[pos] fake = e - print(f"POS {pos} OFFSET: {offset} FAKE VS REAL") + print("POS %d OFFSET: %d FAKE VS REAL" % (pos, offset)) print(fake) print(real) assert fake.opcode == real.opcode diff --git a/setup.py b/setup.py index 7cb39fff..b496e614 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ # twine upload dist/* setup( name='Js2Py', - version='0.72', + version='0.74', packages=['js2py', 'js2py.utils', 'js2py.prototypes', 'js2py.translators', 'js2py.constructors', 'js2py.host', 'js2py.es6', 'js2py.internals',