Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa41dc3f35 |
@@ -1,22 +1,4 @@
|
||||
|
||||
2.6.4.2911
|
||||
- core: improve file cache (windows especially); use fixed-length cache filenames; fixes #600
|
||||
- core: don't log "Checking connections ..." when sonarr/radarr not activated
|
||||
- core: make logging for scanning/parsing/preparing videos more clear
|
||||
- core: extract embedded: continue searching for embbedded subs after undefined language is found (thanks @jippo015)
|
||||
- compat: core: don't assume hints["title"] exists
|
||||
- compat: providers: podnapisi: loosen lxml requirement
|
||||
- providers: addic7ed: fix not using user credentials; fixes #605
|
||||
- providers: titlovi: fix provider (thanks @viking1304)
|
||||
- providers: subscene: fix provider
|
||||
- providers: opensubtitles: improve token logging
|
||||
- providers: podnapisi: fix searching for Marvel series
|
||||
- submod: HI: correctly remove uppercase at start of a sentence when lead by a crocodile (>>)
|
||||
- submod: HI: correctly remove lowercase inside brackets when HI matched as well
|
||||
- submod: HI: remove multiple HI_before_colon_caps before one colon
|
||||
- submod: common: also match music symbols after a crocodile; move crocodile removal up the chain
|
||||
|
||||
|
||||
2.6.4.2881
|
||||
- core: extract embedded: fix automatic extraction not actually writing the subtitles to disk under certain circumstances; fixes #598
|
||||
- core: sonarr/radarr: don't block the main thread while checking connectivity; fixes #597
|
||||
|
||||
@@ -70,7 +70,7 @@ def Start():
|
||||
ValidatePrefs()
|
||||
Log.Debug(config.full_version)
|
||||
|
||||
if not config.permissions_ok:
|
||||
if config.initialized and not config.permissions_ok:
|
||||
Log.Error("Insufficient permissions on library folders:")
|
||||
for title, path in config.missing_permissions:
|
||||
Log.Error("Insufficient permissions on library %s, folder: %s" % (title, path))
|
||||
|
||||
@@ -580,8 +580,6 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
bl_addon = "Blacklisted "
|
||||
|
||||
wrong_fps_addon = ""
|
||||
wrong_series_addon = ""
|
||||
wrong_season_ep_addon = ""
|
||||
if subtitle.wrong_fps:
|
||||
if plex_part:
|
||||
wrong_fps_addon = _(" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)",
|
||||
@@ -591,24 +589,15 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
wrong_fps_addon = _(" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)",
|
||||
subtitle_fps=subtitle.fps)
|
||||
|
||||
if subtitle.wrong_series:
|
||||
wrong_series_addon = _(" (possibly wrong series)")
|
||||
|
||||
if subtitle.wrong_season_ep:
|
||||
wrong_season_ep_addon = _(" (possibly wrong season/episode)")
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerDownloadSubtitle, rating_key=rating_key, randomize=timestamp(), item_title=item_title,
|
||||
subtitle_id=str(subtitle.id), language=language),
|
||||
title=_(u"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s"
|
||||
u"%(wrong_series_state)s%(wrong_season_ep_state)s",
|
||||
title=_(u"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s",
|
||||
blacklisted_state=bl_addon,
|
||||
current_state=_("Available") if current_id != subtitle.id else _("Current"),
|
||||
provider_name=_(subtitle.provider_name),
|
||||
score=subtitle.score,
|
||||
wrong_fps_state=wrong_fps_addon,
|
||||
wrong_series_state=wrong_series_addon,
|
||||
wrong_season_ep_state=wrong_season_ep_addon),
|
||||
wrong_fps_state=wrong_fps_addon),
|
||||
summary=_(u"Release: %(release_info)s, Matches: %(matches)s",
|
||||
release_info=subtitle.release_info,
|
||||
matches=", ".join(subtitle.matches)),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# coding=utf-8
|
||||
|
||||
from subzero.constants import PREFIX, TITLE, ART
|
||||
import time
|
||||
|
||||
from subzero.constants import PREFIX, TITLE, ART, START_DELAY
|
||||
from support.config import config
|
||||
from support.helpers import pad_title, timestamp, df, display_language
|
||||
from support.scheduler import scheduler
|
||||
@@ -27,45 +29,56 @@ def fatality(randomize=None, force_title=None, header=None, message=None, only_r
|
||||
no_history=no_history,
|
||||
replace_parent=replace_parent, no_cache=True)
|
||||
|
||||
# always re-check permissions
|
||||
config.refresh_permissions_status()
|
||||
if config.initialized:
|
||||
# always re-check permissions
|
||||
config.refresh_permissions_status()
|
||||
|
||||
# always re-check enabled sections
|
||||
config.refresh_enabled_sections()
|
||||
# always re-check enabled sections
|
||||
config.refresh_enabled_sections()
|
||||
|
||||
if config.lock_menu and not config.pin_correct:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(PinMenu, randomize=timestamp()),
|
||||
title=pad_title(_("Enter PIN")),
|
||||
summary=_("The owner has restricted the access to this menu. Please enter the correct pin"),
|
||||
))
|
||||
return oc
|
||||
|
||||
if not config.permissions_ok and config.missing_permissions:
|
||||
if not isinstance(config.missing_permissions, list):
|
||||
if config.lock_menu and not config.pin_correct:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title(_("Insufficient permissions")),
|
||||
summary=config.missing_permissions,
|
||||
key=Callback(PinMenu, randomize=timestamp()),
|
||||
title=pad_title(_("Enter PIN")),
|
||||
summary=_("The owner has restricted the access to this menu. Please enter the correct pin"),
|
||||
))
|
||||
else:
|
||||
for title, path in config.missing_permissions:
|
||||
return oc
|
||||
|
||||
if not config.permissions_ok and config.missing_permissions:
|
||||
if not isinstance(config.missing_permissions, list):
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title(_("Insufficient permissions")),
|
||||
summary=_("Insufficient permissions on library %(title)s, folder: %(path)s",
|
||||
title=title,
|
||||
path=path),
|
||||
summary=config.missing_permissions,
|
||||
))
|
||||
return oc
|
||||
else:
|
||||
for title, path in config.missing_permissions:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title(_("Insufficient permissions")),
|
||||
summary=_("Insufficient permissions on library %(title)s, folder: %(path)s",
|
||||
title=title,
|
||||
path=path),
|
||||
))
|
||||
return oc
|
||||
|
||||
if not config.enabled_sections:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title(_("I'm not enabled!")),
|
||||
summary=_("Please enable me for some of your libraries in your server settings; currently I do nothing"),
|
||||
))
|
||||
return oc
|
||||
if not config.enabled_sections:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title(_("I'm not enabled!")),
|
||||
summary=_("Please enable me for some of your libraries in your server settings; currently I do nothing"),
|
||||
))
|
||||
return oc
|
||||
else:
|
||||
if config.delay_system_queries:
|
||||
elapsed = int(START_DELAY - (time.time() - config.start_delay_elapsed))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title(_("Finalizing ..."
|
||||
if elapsed <= 0 else "Initializing, please wait %s seconds ..." % elapsed)),
|
||||
summary=_("Start is delayed by %s seconds to cope with a slow PMS" % int(START_DELAY)),
|
||||
))
|
||||
return oc
|
||||
|
||||
if not only_refresh:
|
||||
if Dict["current_refresh_state"]:
|
||||
|
||||
@@ -367,8 +367,7 @@ def ValidatePrefs():
|
||||
"enable_channel", "permissions_ok", "missing_permissions", "fs_encoding",
|
||||
"subtitle_destination_folder", "include", "include_exclude_paths", "include_exclude_sz_files",
|
||||
"new_style_cache", "dbm_supported", "lang_list", "providers", "normal_subs", "forced_only", "forced_also",
|
||||
"plex_transcoder", "refiner_settings", "unrar", "adv_cfg_path", "use_custom_dns",
|
||||
"has_anticaptcha", "anticaptcha_cls"]:
|
||||
"plex_transcoder", "refiner_settings", "unrar", "adv_cfg_path", "use_custom_dns"]:
|
||||
|
||||
value = getattr(config, attr)
|
||||
if isinstance(value, dict):
|
||||
@@ -376,9 +375,6 @@ def ValidatePrefs():
|
||||
Log.Debug("config.%s: %s", attr, d)
|
||||
continue
|
||||
|
||||
if attr in ("api_key",):
|
||||
value = "xxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
Log.Debug("config.%s: %s", attr, value)
|
||||
|
||||
for attr in ["plugin_log_path", "server_log_path"]:
|
||||
|
||||
@@ -7,12 +7,11 @@ import os
|
||||
import operator
|
||||
|
||||
from func import enable_channel_wrapper, route_wrapper, register_route_function
|
||||
from subzero.lib.io import get_viable_encoding
|
||||
from subzero.language import Language
|
||||
from support.i18n import is_localized_string, _
|
||||
from support.items import get_kind, get_item_thumb, get_item, get_item_kind_from_item, refresh_item
|
||||
from support.helpers import get_video_display_title, pad_title, display_language, quote_args, is_stream_forced, \
|
||||
get_title_for_video_metadata, mswindows
|
||||
get_title_for_video_metadata
|
||||
from support.history import get_history
|
||||
from support.ignore import get_decision_list
|
||||
from support.lib import get_intent
|
||||
@@ -194,16 +193,9 @@ def extract_embedded_sub(**kwargs):
|
||||
args = [
|
||||
config.plex_transcoder, "-i", part.file, "-map", "0:%s" % stream_index, "-f", out_codec, "-"
|
||||
]
|
||||
|
||||
cmdline = quote_args(args)
|
||||
Log.Debug(u"Calling: %s", cmdline)
|
||||
if mswindows:
|
||||
Log.Debug("MSWindows: Fixing encoding")
|
||||
cmdline = cmdline.encode("mbcs")
|
||||
|
||||
output = None
|
||||
try:
|
||||
output = subprocess.check_output(cmdline, stderr=subprocess.PIPE, shell=True)
|
||||
output = subprocess.check_output(quote_args(args), stderr=subprocess.PIPE, shell=True)
|
||||
except:
|
||||
Log.Error("Extraction failed: %s", traceback.format_exc())
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# coding=utf-8
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import inspect
|
||||
@@ -8,6 +7,7 @@ import sys
|
||||
import rarfile
|
||||
import jstyleson
|
||||
import datetime
|
||||
import time
|
||||
|
||||
import subliminal
|
||||
import subliminal_patch
|
||||
@@ -23,7 +23,7 @@ from subliminal.cli import MutexLock
|
||||
from subzero.lib.io import FileIO, get_viable_encoding
|
||||
from subzero.lib.dict import Dicked
|
||||
from subzero.util import get_root_path
|
||||
from subzero.constants import PLUGIN_NAME, PLUGIN_IDENTIFIER, MOVIE, SHOW, MEDIA_TYPE_TO_STRING
|
||||
from subzero.constants import PLUGIN_NAME, PLUGIN_IDENTIFIER, MOVIE, SHOW, MEDIA_TYPE_TO_STRING, START_DELAY
|
||||
from subzero.prefs import get_user_prefs, update_user_prefs
|
||||
from dogpile.cache.region import register_backend as register_cache_backend
|
||||
from lib import Plex
|
||||
@@ -144,19 +144,20 @@ class Config(object):
|
||||
refiner_settings = None
|
||||
exact_filenames = False
|
||||
only_one = False
|
||||
any_language_is_enough = False
|
||||
embedded_auto_extract = False
|
||||
ietf_as_alpha3 = False
|
||||
unrar = None
|
||||
adv_cfg_path = None
|
||||
use_custom_dns = None
|
||||
anticaptcha_token = None
|
||||
anticaptcha_cls = None
|
||||
has_anticaptcha = False
|
||||
use_custom_dns = False
|
||||
delay_system_queries = False
|
||||
|
||||
store_recently_played_amount = 40
|
||||
|
||||
initialized = False
|
||||
system_queries_done = False
|
||||
base_init_done = False
|
||||
system_queries_timer = None
|
||||
start_delay_elapsed = None
|
||||
|
||||
def initialize(self):
|
||||
self.libraries_root = os.path.abspath(os.path.join(get_root_path(), ".."))
|
||||
@@ -174,6 +175,7 @@ class Config(object):
|
||||
self.set_log_paths()
|
||||
self.app_support_path = Core.app_support_path
|
||||
self.data_path = getattr(Data, "_core").storage.data_path
|
||||
self.delay_system_queries = os.path.isfile(os.path.join(self.data_path, "delayed_start"))
|
||||
self.data_items_path = os.path.join(self.data_path, "DataItems")
|
||||
self.universal_plex_token = self.get_universal_plex_token()
|
||||
self.plex_token = os.environ.get("PLEXTOKEN", self.universal_plex_token)
|
||||
@@ -191,11 +193,6 @@ class Config(object):
|
||||
self.debug_i18n = self.advanced.debug_i18n
|
||||
|
||||
os.environ["SZ_USER_AGENT"] = self.get_user_agent()
|
||||
os.environ["ANTICAPTCHA_ACCOUNT_KEY"] = self.anticaptcha_token = str(Prefs["anticaptcha.api_key"]) or ""
|
||||
acs = str(Prefs["anticaptcha.service"])
|
||||
if acs and acs != "none":
|
||||
os.environ["ANTICAPTCHA_CLASS"] = self.anticaptcha_cls = acs
|
||||
self.has_anticaptcha = self.anticaptcha_token and self.anticaptcha_cls
|
||||
|
||||
self.setup_proxies()
|
||||
self.set_plugin_mode()
|
||||
@@ -215,8 +212,25 @@ class Config(object):
|
||||
self.missing_permissions = []
|
||||
self.include_exclude_sz_files = cast_bool(Prefs["subtitles.include_exclude_fs"])
|
||||
self.include_exclude_paths = self.parse_include_exclude_paths()
|
||||
self.enabled_sections = self.check_enabled_sections()
|
||||
self.permissions_ok = self.check_permissions()
|
||||
|
||||
self.system_queries_done = False
|
||||
|
||||
def system_queries():
|
||||
self.enabled_sections = self.check_enabled_sections()
|
||||
self.permissions_ok = self.check_permissions()
|
||||
self.system_queries_done = True
|
||||
self.system_queries_timer = None
|
||||
if self.base_init_done:
|
||||
self.initialized = True
|
||||
|
||||
if self.delay_system_queries:
|
||||
if not self.system_queries_timer or not self.system_queries_timer.is_alive():
|
||||
Log.Info("Waiting %s seconds until querying the system endpoints of your PMS" % START_DELAY)
|
||||
Thread.CreateTimer(START_DELAY, system_queries)
|
||||
self.start_delay_elapsed = time.time()
|
||||
else:
|
||||
system_queries()
|
||||
|
||||
self.notify_executable = self.check_notify_executable()
|
||||
self.remove_hi = cast_bool(Prefs['subtitles.remove_hi'])
|
||||
self.remove_tags = cast_bool(Prefs['subtitles.remove_tags'])
|
||||
@@ -235,11 +249,14 @@ class Config(object):
|
||||
self.no_refresh = os.environ.get("SZ_NO_REFRESH", False)
|
||||
self.plex_transcoder = self.get_plex_transcoder()
|
||||
self.only_one = cast_bool(Prefs['subtitles.only_one'])
|
||||
self.any_language_is_enough = Prefs['subtitles.any_language_is_enough']
|
||||
self.embedded_auto_extract = cast_bool(Prefs["subtitles.embedded.autoextract"])
|
||||
self.ietf_as_alpha3 = cast_bool(Prefs["subtitles.language.ietf_normalize"])
|
||||
self.use_custom_dns = self.parse_custom_dns()
|
||||
self.initialized = True
|
||||
self.use_custom_dns = cast_bool(Prefs['use_custom_dns'])
|
||||
|
||||
self.base_init_done = True
|
||||
|
||||
if self.system_queries_done:
|
||||
self.initialized = True
|
||||
|
||||
def migrate_prefs(self):
|
||||
config_version = 0 if "config_version" not in Dict else Dict["config_version"]
|
||||
@@ -340,7 +357,7 @@ class Config(object):
|
||||
|
||||
def init_cache(self):
|
||||
if self.new_style_cache:
|
||||
subliminal.region.configure('subzero.cache.file', expiration_time=datetime.timedelta(days=180),
|
||||
subliminal.region.configure('subzero.cache.file', expiration_time=datetime.timedelta(days=30),
|
||||
arguments={'appname': "sz_cache",
|
||||
'app_cache_dir': self.data_path},
|
||||
replace_existing_backend=True)
|
||||
@@ -762,7 +779,7 @@ class Config(object):
|
||||
# 'thesubdb': Prefs['provider.thesubdb.enabled'],
|
||||
'podnapisi': cast_bool(Prefs['provider.podnapisi.enabled']),
|
||||
'titlovi': cast_bool(Prefs['provider.titlovi.enabled']),
|
||||
'addic7ed': cast_bool(Prefs['provider.addic7ed.enabled']) and self.has_anticaptcha,
|
||||
'addic7ed': cast_bool(Prefs['provider.addic7ed.enabled']),
|
||||
'tvsubtitles': cast_bool(Prefs['provider.tvsubtitles.enabled']),
|
||||
'legendastv': cast_bool(Prefs['provider.legendastv.enabled']),
|
||||
'napiprojekt': cast_bool(Prefs['provider.napiprojekt.enabled']),
|
||||
@@ -849,6 +866,7 @@ class Config(object):
|
||||
|
||||
provider_settings = {'addic7ed': {'username': Prefs['provider.addic7ed.username'],
|
||||
'password': Prefs['provider.addic7ed.password'],
|
||||
'use_random_agents': cast_bool(Prefs['provider.addic7ed.use_random_agents1']),
|
||||
},
|
||||
'opensubtitles': {'username': Prefs['provider.opensubtitles.username'],
|
||||
'password': Prefs['provider.opensubtitles.password'],
|
||||
@@ -1081,14 +1099,6 @@ class Config(object):
|
||||
def text_based_formats(self):
|
||||
return self.advanced.text_subtitle_formats or TEXT_SUBTITLE_EXTS
|
||||
|
||||
def parse_custom_dns(self):
|
||||
custom_dns = Prefs['use_custom_dns2'].strip()
|
||||
if custom_dns:
|
||||
ips = filter(lambda x: x, [d.strip() for d in custom_dns.split(",")])
|
||||
if ips:
|
||||
os.environ["dns_resolvers"] = json.dumps(ips)
|
||||
return os.environ["dns_resolvers"]
|
||||
|
||||
def init_subliminal_patches(self):
|
||||
# configure custom subtitle destination folders for scanning pre-existing subs
|
||||
Log.Debug("Patching subliminal ...")
|
||||
@@ -1099,6 +1109,9 @@ class Config(object):
|
||||
subliminal_patch.core.DOWNLOAD_TRIES = int(Prefs['subtitles.try_downloads'])
|
||||
subliminal.score.episode_scores["addic7ed_boost"] = int(Prefs['provider.addic7ed.boost_by2'])
|
||||
|
||||
if self.use_custom_dns:
|
||||
subliminal_patch.http.set_custom_resolver()
|
||||
|
||||
|
||||
config = Config()
|
||||
config.initialize()
|
||||
|
||||
@@ -46,22 +46,6 @@ def get_missing_languages(video, part):
|
||||
|
||||
missing_languages = (languages - have_languages)
|
||||
|
||||
if config.any_language_is_enough != "Always search for all configured languages":
|
||||
not_in_forced = "foreign" in config.any_language_is_enough
|
||||
if "External or embedded subtitle" in config.any_language_is_enough:
|
||||
langs = video.subtitle_languages if not not_in_forced else \
|
||||
filter(lambda l: not l.forced, video.subtitle_languages)
|
||||
if langs:
|
||||
Log.Debug("We have at least one subtitle for any configured language.")
|
||||
return False
|
||||
|
||||
elif "External subtitle" in config.any_language_is_enough:
|
||||
langs = video.subtitle_languages if not not_in_forced else \
|
||||
filter(lambda l: not l.forced, video.external_subtitle_languages)
|
||||
if langs:
|
||||
Log.Debug("We have at least one external subtitle for any configured language.")
|
||||
return False
|
||||
|
||||
# all languages are found if we either really have subs for all languages or we only want to have exactly one language
|
||||
# and we've only found one (the case for a selected language, Prefs['subtitles.only_one'] (one found sub matches any language))
|
||||
found_one_which_is_enough = len(video.subtitle_languages) >= 1 and Prefs['subtitles.only_one']
|
||||
|
||||
@@ -286,7 +286,6 @@ def notify_executable(exe_info, videos, subtitles, storage):
|
||||
"subtitle_language", "subtitle_path", "subtitle_filename", "provider", "score", "storage", "series_id",
|
||||
"series", "title", "section", "filename", "path", "folder", "season_id", "type", "id", "season"
|
||||
)
|
||||
to_clean = ("PYTHONPATH", "PYTHONHOME")
|
||||
exe, arguments = exe_info
|
||||
for video, video_subtitles in subtitles.items():
|
||||
for subtitle in video_subtitles:
|
||||
@@ -322,9 +321,8 @@ def notify_executable(exe_info, videos, subtitles, storage):
|
||||
env = dict(os.environ)
|
||||
|
||||
# clean out any Plex-PYTHONPATH that may bleed through the spawned process
|
||||
for v in to_clean:
|
||||
if v in env and "plex" in env[v].lower():
|
||||
del env[v]
|
||||
if "PYTHONPATH" in env and "plex" in env["PYTHONPATH"].lower():
|
||||
del env["PYTHONPATH"]
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(quote_args([exe] + prepared_arguments), stdout=subprocess.PIPE,
|
||||
|
||||
@@ -165,10 +165,8 @@ class SubtitleListingMixin(object):
|
||||
can_verify_series = False
|
||||
|
||||
if can_verify_series and not {"series", "season", "episode"}.issubset(matches):
|
||||
if "series" not in matches:
|
||||
s.wrong_series = True
|
||||
else:
|
||||
s.wrong_season_ep = True
|
||||
Log.Debug(u"%s: Skipping %s, because it doesn't match our series/episode", self.name, s)
|
||||
continue
|
||||
|
||||
unsorted_subtitles.append(
|
||||
(s, compute_score(matches, s, video, hearing_impaired=use_hearing_impaired), matches))
|
||||
@@ -177,7 +175,7 @@ class SubtitleListingMixin(object):
|
||||
subtitles = []
|
||||
for subtitle, score, matches in scored_subtitles:
|
||||
# check score
|
||||
if score < min_score and not subtitle.wrong_series:
|
||||
if score < min_score:
|
||||
Log.Info(u'%s: Score %d is below min_score (%d)', self.name, score, min_score)
|
||||
continue
|
||||
subtitle.score = score
|
||||
|
||||
+13
-37
@@ -205,19 +205,6 @@
|
||||
],
|
||||
"default": "Never"
|
||||
},
|
||||
{
|
||||
"id": "subtitles.any_language_is_enough",
|
||||
"label": "Don't search for subtitles if a subtitle in any configured language exists as",
|
||||
"type": "enum",
|
||||
"values": [
|
||||
"External or embedded subtitle",
|
||||
"External or embedded subtitle (not foreign/forced)",
|
||||
"External subtitle",
|
||||
"External subtitle (not foreign/forced)",
|
||||
"Always search for all configured languages"
|
||||
],
|
||||
"default": "Always search for all configured languages"
|
||||
},
|
||||
{
|
||||
"id": "subtitles.language.ietf_display",
|
||||
"label": "Display languages with country attribute as ISO 639-1 (e.g. pt-BR = pt)",
|
||||
@@ -286,23 +273,6 @@
|
||||
"type": "text",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "anticaptcha.service",
|
||||
"label": "AntiCaptcha-Service (needs paid account; enables Addic7ed, titlovi)",
|
||||
"type": "enum",
|
||||
"values": [
|
||||
"none",
|
||||
"anti-captcha.com",
|
||||
"deathbycaptcha.com"
|
||||
],
|
||||
"default": "none"
|
||||
},
|
||||
{
|
||||
"id": "anticaptcha.api_key",
|
||||
"label": "AntiCaptcha-Service key (anti-captcha.com: account_key; deathbycaptcha.com: username:password)",
|
||||
"type": "text",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "provider.opensubtitles.enabled",
|
||||
"label": "Provider: Enable OpenSubtitles",
|
||||
@@ -335,9 +305,15 @@
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"id": "provider.titlovi.enabled",
|
||||
"label": "Provider: Enable Titlovi.com",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"id": "provider.addic7ed.enabled",
|
||||
"label": "Provider: Enable Addic7ed (needs AntiCaptcha)",
|
||||
"label": "Provider: Enable Addic7ed",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
@@ -388,8 +364,8 @@
|
||||
"default": "19"
|
||||
},
|
||||
{
|
||||
"id": "provider.titlovi.enabled",
|
||||
"label": "Provider: Enable Titlovi.com (might need AntiCaptcha)",
|
||||
"id": "provider.addic7ed.use_random_agents1",
|
||||
"label": "Addic7ed: Use random user agents",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
@@ -860,10 +836,10 @@
|
||||
"default": "15"
|
||||
},
|
||||
{
|
||||
"id": "use_custom_dns2",
|
||||
"label": "Use custom DNS (IPs, comma-separated, leave empty for system DNS. Default: Google/CF)",
|
||||
"type": "text",
|
||||
"default": "1.1.1.1, 8.8.8.8"
|
||||
"id": "use_custom_dns",
|
||||
"label": "Use Google DNS (for \"problematic\" countries)",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "proxy",
|
||||
|
||||
+4
-4
@@ -9,11 +9,11 @@
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.6.5</string>
|
||||
<string>2.6.4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.6.5.3023</string>
|
||||
<string>2.6.4.2934</string>
|
||||
<key>PlexFrameworkVersion</key>
|
||||
<string>2</string>
|
||||
<key>PlexPluginClass</key>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<h1>Sub-Zero for Plex</h1><i>Subtitles done right</i>
|
||||
|
||||
Version 2.6.5.3023 DEV
|
||||
Version 2.6.4.2934 DEV
|
||||
|
||||
Originally based on @bramwalet's awesome <a href="https://github.com/bramwalet/Subliminal.bundle">Subliminal.bundle</a>
|
||||
|
||||
@@ -46,7 +46,7 @@ Github: <a href="https://github.com/pannal/Sub-Zero.bundle">http
|
||||
|
||||
3rd party licenses: <a href="https://github.com/pannal/Sub-Zero.bundle/tree/master/Licenses">https://github.com/pannal/Sub-Zero.bundle/tree/master/Licenses</a>
|
||||
|
||||
panni, 2019
|
||||
panni, 2018
|
||||
</div>
|
||||
</string>
|
||||
</dict>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
from .core import where
|
||||
from .core import where, old_where
|
||||
|
||||
__version__ = "2019.03.09"
|
||||
__version__ = "2018.10.15"
|
||||
|
||||
@@ -4268,391 +4268,3 @@ rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV
|
||||
57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg
|
||||
Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTS Root R1 O=Google Trust Services LLC
|
||||
# Subject: CN=GTS Root R1 O=Google Trust Services LLC
|
||||
# Label: "GTS Root R1"
|
||||
# Serial: 146587175971765017618439757810265552097
|
||||
# MD5 Fingerprint: 82:1a:ef:d4:d2:4a:f2:9f:e2:3d:97:06:14:70:72:85
|
||||
# SHA1 Fingerprint: e1:c9:50:e6:ef:22:f8:4c:56:45:72:8b:92:20:60:d7:d5:a7:a3:e8
|
||||
# SHA256 Fingerprint: 2a:57:54:71:e3:13:40:bc:21:58:1c:bd:2c:f1:3e:15:84:63:20:3e:ce:94:bc:f9:d3:cc:19:6b:f0:9a:54:72
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH
|
||||
MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
|
||||
QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy
|
||||
MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl
|
||||
cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB
|
||||
AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM
|
||||
f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX
|
||||
mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7
|
||||
zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P
|
||||
fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc
|
||||
vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4
|
||||
Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp
|
||||
zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO
|
||||
Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW
|
||||
k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+
|
||||
DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF
|
||||
lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
|
||||
HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW
|
||||
Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1
|
||||
d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z
|
||||
XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR
|
||||
gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3
|
||||
d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv
|
||||
J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg
|
||||
DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM
|
||||
+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy
|
||||
F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9
|
||||
SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws
|
||||
E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTS Root R2 O=Google Trust Services LLC
|
||||
# Subject: CN=GTS Root R2 O=Google Trust Services LLC
|
||||
# Label: "GTS Root R2"
|
||||
# Serial: 146587176055767053814479386953112547951
|
||||
# MD5 Fingerprint: 44:ed:9a:0e:a4:09:3b:00:f2:ae:4c:a3:c6:61:b0:8b
|
||||
# SHA1 Fingerprint: d2:73:96:2a:2a:5e:39:9f:73:3f:e1:c7:1e:64:3f:03:38:34:fc:4d
|
||||
# SHA256 Fingerprint: c4:5d:7b:b0:8e:6d:67:e6:2e:42:35:11:0b:56:4e:5f:78:fd:92:ef:05:8c:84:0a:ea:4e:64:55:d7:58:5c:60
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH
|
||||
MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
|
||||
QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy
|
||||
MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl
|
||||
cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB
|
||||
AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv
|
||||
CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg
|
||||
GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu
|
||||
XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd
|
||||
re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu
|
||||
PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1
|
||||
mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K
|
||||
8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj
|
||||
x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR
|
||||
nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0
|
||||
kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok
|
||||
twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
|
||||
HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp
|
||||
8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT
|
||||
vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT
|
||||
z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA
|
||||
pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb
|
||||
pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB
|
||||
R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R
|
||||
RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk
|
||||
0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC
|
||||
5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF
|
||||
izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn
|
||||
yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTS Root R3 O=Google Trust Services LLC
|
||||
# Subject: CN=GTS Root R3 O=Google Trust Services LLC
|
||||
# Label: "GTS Root R3"
|
||||
# Serial: 146587176140553309517047991083707763997
|
||||
# MD5 Fingerprint: 1a:79:5b:6b:04:52:9c:5d:c7:74:33:1b:25:9a:f9:25
|
||||
# SHA1 Fingerprint: 30:d4:24:6f:07:ff:db:91:89:8a:0b:e9:49:66:11:eb:8c:5e:46:e5
|
||||
# SHA256 Fingerprint: 15:d5:b8:77:46:19:ea:7d:54:ce:1c:a6:d0:b0:c4:03:e0:37:a9:17:f1:31:e8:a0:4e:1e:6b:7a:71:ba:bc:e5
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout
|
||||
736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A
|
||||
DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk
|
||||
fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA
|
||||
njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTS Root R4 O=Google Trust Services LLC
|
||||
# Subject: CN=GTS Root R4 O=Google Trust Services LLC
|
||||
# Label: "GTS Root R4"
|
||||
# Serial: 146587176229350439916519468929765261721
|
||||
# MD5 Fingerprint: 5d:b6:6a:c4:60:17:24:6a:1a:99:a8:4b:ee:5e:b4:26
|
||||
# SHA1 Fingerprint: 2a:1d:60:27:d9:4a:b1:0a:1c:4d:91:5c:cd:33:a0:cb:3e:2d:54:cb
|
||||
# SHA256 Fingerprint: 71:cc:a5:39:1f:9e:79:4b:04:80:25:30:b3:63:e1:21:da:8a:30:43:bb:26:66:2f:ea:4d:ca:7f:c9:51:a4:bd
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu
|
||||
hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l
|
||||
xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0
|
||||
CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx
|
||||
sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=UCA Global G2 Root O=UniTrust
|
||||
# Subject: CN=UCA Global G2 Root O=UniTrust
|
||||
# Label: "UCA Global G2 Root"
|
||||
# Serial: 124779693093741543919145257850076631279
|
||||
# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8
|
||||
# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a
|
||||
# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9
|
||||
MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH
|
||||
bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x
|
||||
CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds
|
||||
b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr
|
||||
b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9
|
||||
kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm
|
||||
VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R
|
||||
VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc
|
||||
C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj
|
||||
tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY
|
||||
D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv
|
||||
j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl
|
||||
NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6
|
||||
iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP
|
||||
O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/
|
||||
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV
|
||||
ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj
|
||||
L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5
|
||||
1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl
|
||||
1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU
|
||||
b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV
|
||||
PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj
|
||||
y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb
|
||||
EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg
|
||||
DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI
|
||||
+Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy
|
||||
YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX
|
||||
UB+K+wb1whnw0A==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=UCA Extended Validation Root O=UniTrust
|
||||
# Subject: CN=UCA Extended Validation Root O=UniTrust
|
||||
# Label: "UCA Extended Validation Root"
|
||||
# Serial: 106100277556486529736699587978573607008
|
||||
# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2
|
||||
# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a
|
||||
# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH
|
||||
MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF
|
||||
eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx
|
||||
MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV
|
||||
BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB
|
||||
AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog
|
||||
D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS
|
||||
sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop
|
||||
O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk
|
||||
sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi
|
||||
c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj
|
||||
VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz
|
||||
KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/
|
||||
TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G
|
||||
sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs
|
||||
1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD
|
||||
fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T
|
||||
AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN
|
||||
l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR
|
||||
ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ
|
||||
VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5
|
||||
c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp
|
||||
4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s
|
||||
t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj
|
||||
2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO
|
||||
vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C
|
||||
xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx
|
||||
cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM
|
||||
fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036
|
||||
# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036
|
||||
# Label: "Certigna Root CA"
|
||||
# Serial: 269714418870597844693661054334862075617
|
||||
# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77
|
||||
# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43
|
||||
# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw
|
||||
WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw
|
||||
MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x
|
||||
MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD
|
||||
VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX
|
||||
BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
|
||||
ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO
|
||||
ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M
|
||||
CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu
|
||||
I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm
|
||||
TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh
|
||||
C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf
|
||||
ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz
|
||||
IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT
|
||||
Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k
|
||||
JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5
|
||||
hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB
|
||||
GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
|
||||
FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of
|
||||
1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov
|
||||
L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo
|
||||
dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr
|
||||
aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L
|
||||
6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG
|
||||
HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6
|
||||
0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB
|
||||
lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi
|
||||
o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1
|
||||
gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v
|
||||
faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63
|
||||
Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh
|
||||
jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw
|
||||
3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI
|
||||
# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI
|
||||
# Label: "emSign Root CA - G1"
|
||||
# Serial: 235931866688319308814040
|
||||
# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac
|
||||
# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c
|
||||
# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD
|
||||
VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU
|
||||
ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH
|
||||
MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO
|
||||
MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv
|
||||
Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz
|
||||
f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO
|
||||
8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq
|
||||
d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM
|
||||
tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt
|
||||
Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB
|
||||
o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD
|
||||
AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x
|
||||
PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM
|
||||
wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d
|
||||
GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH
|
||||
6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby
|
||||
RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx
|
||||
iN66zB+Afko=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
|
||||
# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
|
||||
# Label: "emSign ECC Root CA - G3"
|
||||
# Serial: 287880440101571086945156
|
||||
# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40
|
||||
# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1
|
||||
# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG
|
||||
EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo
|
||||
bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
|
||||
RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ
|
||||
TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s
|
||||
b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw
|
||||
djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0
|
||||
WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS
|
||||
fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB
|
||||
zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq
|
||||
hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB
|
||||
CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD
|
||||
+JbNR6iC8hZVdyR+EhCVBCyj
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
|
||||
# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
|
||||
# Label: "emSign Root CA - C1"
|
||||
# Serial: 825510296613316004955058
|
||||
# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68
|
||||
# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01
|
||||
# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG
|
||||
A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg
|
||||
SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw
|
||||
MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
|
||||
biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v
|
||||
dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ
|
||||
BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ
|
||||
HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH
|
||||
3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH
|
||||
GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c
|
||||
xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1
|
||||
aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq
|
||||
TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||
BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87
|
||||
/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4
|
||||
kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG
|
||||
YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT
|
||||
+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo
|
||||
WXzhriKi4gp6D/piq1JM4fHfyr6DDUI=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI
|
||||
# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI
|
||||
# Label: "emSign ECC Root CA - C3"
|
||||
# Serial: 582948710642506000014504
|
||||
# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5
|
||||
# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66
|
||||
# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG
|
||||
EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx
|
||||
IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw
|
||||
MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
|
||||
biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND
|
||||
IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci
|
||||
MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti
|
||||
sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O
|
||||
BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB
|
||||
Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c
|
||||
3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J
|
||||
0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post
|
||||
# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post
|
||||
# Label: "Hongkong Post Root CA 3"
|
||||
# Serial: 46170865288971385588281144162979347873371282084
|
||||
# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0
|
||||
# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02
|
||||
# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL
|
||||
BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ
|
||||
SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n
|
||||
a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5
|
||||
NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT
|
||||
CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u
|
||||
Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||
AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO
|
||||
dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI
|
||||
VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV
|
||||
9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY
|
||||
2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY
|
||||
vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt
|
||||
bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb
|
||||
x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+
|
||||
l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK
|
||||
TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj
|
||||
Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP
|
||||
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e
|
||||
i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw
|
||||
DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG
|
||||
7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk
|
||||
MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr
|
||||
gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk
|
||||
GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS
|
||||
3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm
|
||||
Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+
|
||||
l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c
|
||||
JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP
|
||||
L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa
|
||||
LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG
|
||||
mpv0
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
@@ -7,9 +8,30 @@ certifi.py
|
||||
This module returns the installation location of cacert.pem.
|
||||
"""
|
||||
import os
|
||||
import warnings
|
||||
|
||||
|
||||
class DeprecatedBundleWarning(DeprecationWarning):
|
||||
"""
|
||||
The weak security bundle is being deprecated. Please bother your service
|
||||
provider to get them to stop using cross-signed roots.
|
||||
"""
|
||||
|
||||
|
||||
def where():
|
||||
f = os.path.dirname(__file__)
|
||||
|
||||
return os.path.join(f, 'cacert.pem')
|
||||
|
||||
|
||||
def old_where():
|
||||
warnings.warn(
|
||||
"The weak security bundle has been removed. certifi.old_where() is now an alias "
|
||||
"of certifi.where(). Please update your code to use certifi.where() instead. "
|
||||
"certifi.old_where() will be removed in 2018.",
|
||||
DeprecatedBundleWarning
|
||||
)
|
||||
return where()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(where())
|
||||
|
||||
@@ -1,392 +0,0 @@
|
||||
# coding=utf-8
|
||||
|
||||
import logging
|
||||
import random
|
||||
import re
|
||||
import os
|
||||
import json
|
||||
import base64
|
||||
|
||||
from copy import deepcopy
|
||||
from time import sleep
|
||||
from collections import OrderedDict
|
||||
from .jsfuck import jsunfuck
|
||||
|
||||
import js2py
|
||||
from requests.sessions import Session
|
||||
from subliminal_patch.pitcher import pitchers
|
||||
|
||||
try:
|
||||
from requests_toolbelt.utils import dump
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
from urlparse import urlparse
|
||||
from urlparse import urlunparse
|
||||
except ImportError:
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import urlunparse
|
||||
|
||||
brotli_available = True
|
||||
|
||||
try:
|
||||
from brotli import decompress as brdec
|
||||
except:
|
||||
brotli_available = False
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__version__ = "2.0.3"
|
||||
|
||||
# Orignally written by https://github.com/Anorov/cloudflare-scrape
|
||||
# Rewritten by VeNoMouS - <venom@gen-x.co.nz> for https://github.com/VeNoMouS/Sick-Beard - 24/3/2018 NZDT
|
||||
|
||||
DEFAULT_USER_AGENTS = [
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/65.0.3325.181 Chrome/65.0.3325.181 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; Moto G (5) Build/NPPS25.137-93-8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:59.0) Gecko/20100101 Firefox/59.0",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0",
|
||||
]
|
||||
|
||||
BUG_REPORT = """\
|
||||
Cloudflare may have changed their technique, or there may be a bug in the script.
|
||||
"""
|
||||
|
||||
|
||||
cur_path = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
if brotli_available:
|
||||
brwsrs = os.path.join(cur_path, "browsers_br.json")
|
||||
with open(brwsrs, "r") as f:
|
||||
UA_COMBO = json.load(f, object_pairs_hook=OrderedDict)["chrome"]
|
||||
|
||||
else:
|
||||
brwsrs = os.path.join(cur_path, "browsers.json")
|
||||
UA_COMBO = []
|
||||
with open(brwsrs, "r") as f:
|
||||
_brwsrs = json.load(f, object_pairs_hook=OrderedDict)
|
||||
for entry in _brwsrs:
|
||||
_entry = OrderedDict(("-".join(a.capitalize() for a in key.split("-")), value)
|
||||
for key, value in entry.iteritems())
|
||||
_entry["User-Agent"] = None
|
||||
UA_COMBO.append({"User-Agent": [entry["user-agent"]], "headers": _entry})
|
||||
|
||||
|
||||
class NeedsCaptchaException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CloudflareScraper(Session):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.delay = kwargs.pop('delay', 8)
|
||||
self.debug = False
|
||||
self._ua = None
|
||||
self._hdrs = None
|
||||
|
||||
super(CloudflareScraper, self).__init__(*args, **kwargs)
|
||||
|
||||
if not self._ua:
|
||||
# Set a random User-Agent if no custom User-Agent has been set
|
||||
ua_combo = random.choice(UA_COMBO)
|
||||
self._ua = random.choice(ua_combo["User-Agent"])
|
||||
self._hdrs = ua_combo["headers"].copy()
|
||||
self._hdrs["User-Agent"] = self._ua
|
||||
self.headers['User-Agent'] = self._ua
|
||||
|
||||
def set_cloudflare_challenge_delay(self, delay):
|
||||
if isinstance(delay, (int, float)) and delay > 0:
|
||||
self.delay = delay
|
||||
|
||||
def is_cloudflare_challenge(self, resp):
|
||||
if resp.headers.get('Server', '').startswith('cloudflare'):
|
||||
if b'why_captcha' in resp.content or b'/cdn-cgi/l/chk_captcha' in resp.content:
|
||||
raise NeedsCaptchaException
|
||||
|
||||
return (
|
||||
resp.status_code in [429, 503]
|
||||
and b"jschl_vc" in resp.content
|
||||
and b"jschl_answer" in resp.content
|
||||
)
|
||||
return False
|
||||
|
||||
def debugRequest(self, req):
|
||||
try:
|
||||
print (dump.dump_all(req).decode('utf-8'))
|
||||
except:
|
||||
pass
|
||||
|
||||
def request(self, method, url, *args, **kwargs):
|
||||
# self.headers = (
|
||||
# OrderedDict(
|
||||
# [
|
||||
# ('User-Agent', self.headers['User-Agent']),
|
||||
# ('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'),
|
||||
# ('Accept-Language', 'en-US,en;q=0.5'),
|
||||
# ('Accept-Encoding', 'gzip, deflate'),
|
||||
# ('Connection', 'close'),
|
||||
# ('Upgrade-Insecure-Requests', '1')
|
||||
# ]
|
||||
# )
|
||||
# )
|
||||
self.headers = self._hdrs.copy()
|
||||
|
||||
resp = super(CloudflareScraper, self).request(method, url, *args, **kwargs)
|
||||
if resp.headers.get('content-encoding') == 'br' and brotli_available:
|
||||
resp._content = brdec(resp._content)
|
||||
|
||||
# Debug request
|
||||
if self.debug:
|
||||
self.debugRequest(resp)
|
||||
|
||||
# Check if Cloudflare anti-bot is on
|
||||
try:
|
||||
if self.is_cloudflare_challenge(resp):
|
||||
# Work around if the initial request is not a GET,
|
||||
# Superseed with a GET then re-request the orignal METHOD.
|
||||
if resp.request.method != 'GET':
|
||||
self.request('GET', resp.url)
|
||||
resp = self.request(method, url, *args, **kwargs)
|
||||
else:
|
||||
resp = self.solve_cf_challenge(resp, **kwargs)
|
||||
except NeedsCaptchaException:
|
||||
# solve the captcha
|
||||
site_key = re.search(r'data-sitekey="(.+?)"', resp.content).group(1)
|
||||
challenge_s = re.search(r'type="hidden" name="s" value="(.+?)"', resp.content).group(1)
|
||||
challenge_ray = re.search(r'data-ray="(.+?)"', resp.content).group(1)
|
||||
if not all([site_key, challenge_s, challenge_ray]):
|
||||
raise Exception("cf: Captcha site-key not found!")
|
||||
|
||||
pitcher = pitchers.get_pitcher()("cf", resp.request.url, site_key,
|
||||
user_agent=self.headers["User-Agent"],
|
||||
cookies=self.cookies.get_dict(),
|
||||
is_invisible=True)
|
||||
|
||||
logger.info("cf: Solving captcha")
|
||||
result = pitcher.throw()
|
||||
if not result:
|
||||
raise Exception("cf: Couldn't solve captcha!")
|
||||
|
||||
parsed_url = urlparse(resp.url)
|
||||
domain = parsed_url.netloc
|
||||
submit_url = '{}://{}/cdn-cgi/l/chk_captcha'.format(parsed_url.scheme, domain)
|
||||
method = resp.request.method
|
||||
|
||||
cloudflare_kwargs = {
|
||||
'allow_redirects': False,
|
||||
'headers': {'Referer': resp.url},
|
||||
'params': OrderedDict(
|
||||
[
|
||||
('s', challenge_s),
|
||||
('g-recaptcha-response', result)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
return self.request(method, submit_url, **cloudflare_kwargs)
|
||||
|
||||
return resp
|
||||
|
||||
def solve_cf_challenge(self, resp, **original_kwargs):
|
||||
body = resp.text
|
||||
|
||||
# Cloudflare requires a delay before solving the challenge
|
||||
if self.delay == 8:
|
||||
try:
|
||||
delay = float(re.search(r'submit\(\);\r?\n\s*},\s*([0-9]+)', body).group(1)) / float(1000)
|
||||
if isinstance(delay, (int, float)):
|
||||
self.delay = delay
|
||||
except:
|
||||
pass
|
||||
|
||||
sleep(self.delay)
|
||||
|
||||
parsed_url = urlparse(resp.url)
|
||||
domain = parsed_url.netloc
|
||||
submit_url = '{}://{}/cdn-cgi/l/chk_jschl'.format(parsed_url.scheme, domain)
|
||||
|
||||
cloudflare_kwargs = deepcopy(original_kwargs)
|
||||
headers = cloudflare_kwargs.setdefault('headers', {'Referer': resp.url})
|
||||
|
||||
try:
|
||||
params = cloudflare_kwargs.setdefault(
|
||||
'params', OrderedDict(
|
||||
[
|
||||
('s', re.search(r'name="s"\svalue="(?P<s_value>[^"]+)', body).group('s_value')),
|
||||
('jschl_vc', re.search(r'name="jschl_vc" value="(\w+)"', body).group(1)),
|
||||
('pass', re.search(r'name="pass" value="(.+?)"', body).group(1)),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
# Something is wrong with the page.
|
||||
# This may indicate Cloudflare has changed their anti-bot
|
||||
# technique. If you see this and are running the latest version,
|
||||
# please open a GitHub issue so I can update the code accordingly.
|
||||
raise ValueError("Unable to parse Cloudflare anti-bots page: {} {}".format(e.message, BUG_REPORT))
|
||||
|
||||
# Solve the Javascript challenge
|
||||
params['jschl_answer'] = self.solve_challenge(body, domain)
|
||||
|
||||
# Requests transforms any request into a GET after a redirect,
|
||||
# so the redirect has to be handled manually here to allow for
|
||||
# performing other types of requests even as the first request.
|
||||
method = resp.request.method
|
||||
|
||||
cloudflare_kwargs['allow_redirects'] = False
|
||||
|
||||
redirect = self.request(method, submit_url, **cloudflare_kwargs)
|
||||
redirect_location = urlparse(redirect.headers['Location'])
|
||||
if not redirect_location.netloc:
|
||||
redirect_url = urlunparse(
|
||||
(
|
||||
parsed_url.scheme,
|
||||
domain,
|
||||
redirect_location.path,
|
||||
redirect_location.params,
|
||||
redirect_location.query,
|
||||
redirect_location.fragment
|
||||
)
|
||||
)
|
||||
return self.request(method, redirect_url, **original_kwargs)
|
||||
|
||||
return self.request(method, redirect.headers['Location'], **original_kwargs)
|
||||
|
||||
def solve_challenge(self, body, domain):
|
||||
try:
|
||||
js = re.search(
|
||||
r"setTimeout\(function\(\){\s+(var s,t,o,p,b,r,e,a,k,i,n,g,f.+?\r?\n[\s\S]+?a\.value =.+?)\r?\n",
|
||||
body
|
||||
).group(1)
|
||||
except Exception:
|
||||
raise ValueError("Unable to identify Cloudflare IUAM Javascript on website. {}".format(BUG_REPORT))
|
||||
|
||||
js = re.sub(r"a\.value = ((.+).toFixed\(10\))?", r"\1", js)
|
||||
js = re.sub(r'(e\s=\sfunction\(s\)\s{.*?};)', '', js, flags=re.DOTALL|re.MULTILINE)
|
||||
js = re.sub(r"\s{3,}[a-z](?: = |\.).+", "", js).replace("t.length", str(len(domain)))
|
||||
|
||||
js = js.replace('; 121', '')
|
||||
|
||||
# Strip characters that could be used to exit the string context
|
||||
# These characters are not currently used in Cloudflare's arithmetic snippet
|
||||
js = re.sub(r"[\n\\']", "", js)
|
||||
|
||||
if 'toFixed' not in js:
|
||||
raise ValueError("Error parsing Cloudflare IUAM Javascript challenge. {}".format(BUG_REPORT))
|
||||
|
||||
try:
|
||||
jsEnv = """
|
||||
var t = "{domain}";
|
||||
var g = String.fromCharCode;
|
||||
|
||||
o = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
e = function(s) {{
|
||||
s += "==".slice(2 - (s.length & 3));
|
||||
var bm, r = "", r1, r2, i = 0;
|
||||
for (; i < s.length;) {{
|
||||
bm = o.indexOf(s.charAt(i++)) << 18 | o.indexOf(s.charAt(i++)) << 12 | (r1 = o.indexOf(s.charAt(i++))) << 6 | (r2 = o.indexOf(s.charAt(i++)));
|
||||
r += r1 === 64 ? g(bm >> 16 & 255) : r2 === 64 ? g(bm >> 16 & 255, bm >> 8 & 255) : g(bm >> 16 & 255, bm >> 8 & 255, bm & 255);
|
||||
}}
|
||||
return r;
|
||||
}};
|
||||
|
||||
function italics (str) {{ return '<i>' + this + '</i>'; }};
|
||||
var document = {{
|
||||
getElementById: function () {{
|
||||
return {{'innerHTML': '{innerHTML}'}};
|
||||
}}
|
||||
}};
|
||||
{js}
|
||||
"""
|
||||
|
||||
innerHTML = re.search(
|
||||
'<div(?: [^<>]*)? id="([^<>]*?)">([^<>]*?)<\/div>',
|
||||
body,
|
||||
re.MULTILINE | re.DOTALL
|
||||
)
|
||||
innerHTML = innerHTML.group(2).replace("'", r"\'") if innerHTML else ""
|
||||
|
||||
js = jsunfuck(jsEnv.format(domain=domain, innerHTML=innerHTML, js=js))
|
||||
|
||||
def atob(s):
|
||||
return base64.b64decode('{}'.format(s)).decode('utf-8')
|
||||
|
||||
js2py.disable_pyimport()
|
||||
context = js2py.EvalJs({'atob': atob})
|
||||
result = context.eval(js)
|
||||
except Exception:
|
||||
logging.error("Error executing Cloudflare IUAM Javascript. {}".format(BUG_REPORT))
|
||||
raise
|
||||
|
||||
try:
|
||||
float(result)
|
||||
except Exception:
|
||||
raise ValueError("Cloudflare IUAM challenge returned unexpected answer. {}".format(BUG_REPORT))
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def create_scraper(cls, sess=None, **kwargs):
|
||||
"""
|
||||
Convenience function for creating a ready-to-go CloudflareScraper object.
|
||||
"""
|
||||
scraper = cls(**kwargs)
|
||||
|
||||
if sess:
|
||||
attrs = ['auth', 'cert', 'cookies', 'headers', 'hooks', 'params', 'proxies', 'data']
|
||||
for attr in attrs:
|
||||
val = getattr(sess, attr, None)
|
||||
if val:
|
||||
setattr(scraper, attr, val)
|
||||
|
||||
return scraper
|
||||
|
||||
# Functions for integrating cloudflare-scrape with other applications and scripts
|
||||
@classmethod
|
||||
def get_tokens(cls, url, user_agent=None, debug=False, **kwargs):
|
||||
scraper = cls.create_scraper()
|
||||
scraper.debug = debug
|
||||
|
||||
if user_agent:
|
||||
scraper.headers['User-Agent'] = user_agent
|
||||
|
||||
try:
|
||||
resp = scraper.get(url, **kwargs)
|
||||
resp.raise_for_status()
|
||||
except Exception as e:
|
||||
logging.error("'{}' returned an error. Could not collect tokens.".format(url))
|
||||
raise
|
||||
|
||||
domain = urlparse(resp.url).netloc
|
||||
cookie_domain = None
|
||||
|
||||
for d in scraper.cookies.list_domains():
|
||||
if d.startswith('.') and d in ('.{}'.format(domain)):
|
||||
cookie_domain = d
|
||||
break
|
||||
else:
|
||||
raise ValueError("Unable to find Cloudflare cookies. Does the site actually have Cloudflare IUAM (\"I'm Under Attack Mode\") enabled?")
|
||||
|
||||
return (
|
||||
{
|
||||
'__cfduid': scraper.cookies.get('__cfduid', '', domain=cookie_domain),
|
||||
'cf_clearance': scraper.cookies.get('cf_clearance', '', domain=cookie_domain)
|
||||
},
|
||||
scraper.headers['User-Agent']
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_cookie_string(cls, url, user_agent=None, debug=False, **kwargs):
|
||||
"""
|
||||
Convenience function for building a Cookie HTTP header value.
|
||||
"""
|
||||
tokens, user_agent = cls.get_tokens(url, user_agent=user_agent, debug=debug, **kwargs)
|
||||
return "; ".join("=".join(pair) for pair in tokens.items()), user_agent
|
||||
|
||||
create_scraper = CloudflareScraper.create_scraper
|
||||
get_tokens = CloudflareScraper.get_tokens
|
||||
get_cookie_string = CloudflareScraper.get_cookie_string
|
||||
@@ -1,80 +0,0 @@
|
||||
[
|
||||
{
|
||||
"connection": "close",
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.102 Safari/537.36",
|
||||
"accept-encoding": "gzip,deflate",
|
||||
"accept-language": "en-US,en;q=0.8"
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36",
|
||||
"accept-encoding": "gzip,deflate",
|
||||
"accept-language": "en-US,en;q=0.8"
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"upgrade-insecure-requests": "1",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36",
|
||||
"accept-language": "en-US,en;q=0.8",
|
||||
"accept-encoding": "gzip, deflate, "
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"upgrade-insecure-requests": "1",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36",
|
||||
"accept-language": "en-US,en;q=0.8",
|
||||
"accept-encoding": "gzip, deflate, "
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"accept": "*/*",
|
||||
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:30.0) Gecko/20100101 Firefox/30.0"
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"accept": "image/jpeg, image/gif, image/pjpeg, application/x-ms-application, application/xaml+xml, application/x-ms-xbap, */*",
|
||||
"accept-language": "en-US",
|
||||
"user-agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E)",
|
||||
"accept-encoding": "gzip, deflate"
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"accept": "text/html, application/xhtml+xml, */*",
|
||||
"accept-language": "en-US",
|
||||
"user-agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)",
|
||||
"accept-encoding": "gzip, deflate"
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"accept": "text/html, application/xhtml+xml, */*",
|
||||
"accept-language": "en-US",
|
||||
"user-agent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)",
|
||||
"accept-encoding": "gzip, deflate",
|
||||
"dnt": "1"
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0",
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"accept-language": "en-US,en;q=0.5",
|
||||
"accept-encoding": "gzip, deflate"
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0",
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"accept-language": "en-US,en;q=0.5",
|
||||
"accept-encoding": "gzip, deflate"
|
||||
},
|
||||
{
|
||||
"connection": "close",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0",
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"accept-language": "en-US,en;q=0.5",
|
||||
"accept-encoding": "gzip, deflate"
|
||||
}
|
||||
]
|
||||
@@ -1,336 +0,0 @@
|
||||
{
|
||||
"chrome": [
|
||||
{
|
||||
"User-Agent": [
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.113 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36"
|
||||
],
|
||||
"headers": {
|
||||
"Connection": "keep-alive",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": null,
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"Accept-Language": "en-US,en;q=0.8",
|
||||
"Accept-Encoding": "gzip, deflate, , br"
|
||||
}
|
||||
},
|
||||
{
|
||||
"User-Agent": [
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
|
||||
],
|
||||
"headers": {
|
||||
"Connection": "keep-alive",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": null,
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||
"Accept-Language": "en-US,en;q=0.8",
|
||||
"Accept-Encoding": "gzip, deflate, br"
|
||||
}
|
||||
},
|
||||
{
|
||||
"User-Agent": [
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36"
|
||||
],
|
||||
"headers": {
|
||||
"Connection": "keep-alive",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": null,
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||
"Accept-Language": "en-US,en;q=0.9",
|
||||
"Accept-Encoding": "gzip, deflate, br"
|
||||
}
|
||||
},
|
||||
{
|
||||
"User-Agent": [
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"
|
||||
],
|
||||
"headers": {
|
||||
"Connection": "keep-alive",
|
||||
"User-Agent": null,
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||
"Accept-Language": "en-US,en;q=0.9",
|
||||
"Accept-Encoding": "gzip, deflate, br"
|
||||
}
|
||||
},
|
||||
{
|
||||
"User-Agent": [
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.40 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.40 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.28 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.28 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.28 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.28 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.28 Safari/537.36"
|
||||
],
|
||||
"headers": {
|
||||
"Connection": "keep-alive",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": null,
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
|
||||
"Accept-Language": "en-US,en;q=0.9",
|
||||
"Accept-Encoding": "gzip, deflate, br"
|
||||
}
|
||||
},
|
||||
{
|
||||
"User-Agent": [
|
||||
"Mozilla/5.0 (Linux; Android 8.1.0; SM-N960F Build/M1AJQ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; SM-G965F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 Build/OPD1.170816.010) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; Pixel Build/OPR6.170623.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.1; SM-A530F Build/NMF26X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1; Pixel Build/NDE63H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G955F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G950F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-T825 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G930F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; Nexus 6 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; XT1092 Build/MPE24.49-18) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-N910C Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.0.2; SM-G920F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.0; Nexus 6 Build/LRX21O) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 9; Pixel 3 XL Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 9; Pixel 2 Build/PPR1.180610.009) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/KRT16M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; SM-T530 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.4; SM-N910C Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 9 Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.1; SM-N950F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36"
|
||||
],
|
||||
"headers": {
|
||||
"Connection": "keep-alive",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": null,
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Accept-Language": "en-US,en;q=0.9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"User-Agent": [
|
||||
"Mozilla/5.0 (Linux; Android 8.1.0; SM-T835 Build/M1AJQ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.0; XT1092 Build/LXE22.46-19) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36"
|
||||
],
|
||||
"headers": {
|
||||
"Connection": "keep-alive",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": null,
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
MAPPING = {
|
||||
'a': '(false+"")[1]',
|
||||
'b': '([]["entries"]()+"")[2]',
|
||||
'c': '([]["fill"]+"")[3]',
|
||||
'd': '(undefined+"")[2]',
|
||||
'e': '(true+"")[3]',
|
||||
'f': '(false+"")[0]',
|
||||
'g': '(false+[0]+String)[20]',
|
||||
'h': '(+(101))["to"+String["name"]](21)[1]',
|
||||
'i': '([false]+undefined)[10]',
|
||||
'j': '([]["entries"]()+"")[3]',
|
||||
'k': '(+(20))["to"+String["name"]](21)',
|
||||
'l': '(false+"")[2]',
|
||||
'm': '(Number+"")[11]',
|
||||
'n': '(undefined+"")[1]',
|
||||
'o': '(true+[]["fill"])[10]',
|
||||
'p': '(+(211))["to"+String["name"]](31)[1]',
|
||||
'q': '(+(212))["to"+String["name"]](31)[1]',
|
||||
'r': '(true+"")[1]',
|
||||
's': '(false+"")[3]',
|
||||
't': '(true+"")[0]',
|
||||
'u': '(undefined+"")[0]',
|
||||
'v': '(+(31))["to"+String["name"]](32)',
|
||||
'w': '(+(32))["to"+String["name"]](33)',
|
||||
'x': '(+(101))["to"+String["name"]](34)[1]',
|
||||
'y': '(NaN+[Infinity])[10]',
|
||||
'z': '(+(35))["to"+String["name"]](36)',
|
||||
'A': '(+[]+Array)[10]',
|
||||
'B': '(+[]+Boolean)[10]',
|
||||
'C': 'Function("return escape")()(("")["italics"]())[2]',
|
||||
'D': 'Function("return escape")()([]["fill"])["slice"]("-1")',
|
||||
'E': '(RegExp+"")[12]',
|
||||
'F': '(+[]+Function)[10]',
|
||||
'G': '(false+Function("return Date")()())[30]',
|
||||
'I': '(Infinity+"")[0]',
|
||||
'M': '(true+Function("return Date")()())[30]',
|
||||
'N': '(NaN+"")[0]',
|
||||
'O': '(NaN+Function("return{}")())[11]',
|
||||
'R': '(+[]+RegExp)[10]',
|
||||
'S': '(+[]+String)[10]',
|
||||
'T': '(NaN+Function("return Date")()())[30]',
|
||||
'U': '(NaN+Function("return{}")()["to"+String["name"]]["call"]())[11]',
|
||||
' ': '(NaN+[]["fill"])[11]',
|
||||
'"': '("")["fontcolor"]()[12]',
|
||||
'%': 'Function("return escape")()([]["fill"])[21]',
|
||||
'&': '("")["link"](0+")[10]',
|
||||
'(': '(undefined+[]["fill"])[22]',
|
||||
')': '([0]+false+[]["fill"])[20]',
|
||||
'+': '(+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]])+[])[2]',
|
||||
',': '([]["slice"]["call"](false+"")+"")[1]',
|
||||
'-': '(+(.+[0000000001])+"")[2]',
|
||||
'.': '(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]',
|
||||
'/': '(false+[0])["italics"]()[10]',
|
||||
':': '(RegExp()+"")[3]',
|
||||
';': '("")["link"](")[14]',
|
||||
'<': '("")["italics"]()[0]',
|
||||
'=': '("")["fontcolor"]()[11]',
|
||||
'>': '("")["italics"]()[2]',
|
||||
'?': '(RegExp()+"")[2]',
|
||||
'[': '([]["entries"]()+"")[0]',
|
||||
']': '([]["entries"]()+"")[22]',
|
||||
'{': '(true+[]["fill"])[20]',
|
||||
'}': '([]["fill"]+"")["slice"]("-1")'
|
||||
}
|
||||
|
||||
SIMPLE = {
|
||||
'false': '![]',
|
||||
'true': '!![]',
|
||||
'undefined': '[][[]]',
|
||||
'NaN': '+[![]]',
|
||||
'Infinity': '+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])' # +"1e1000"
|
||||
}
|
||||
|
||||
CONSTRUCTORS = {
|
||||
'Array': '[]',
|
||||
'Number': '(+[])',
|
||||
'String': '([]+[])',
|
||||
'Boolean': '(![])',
|
||||
'Function': '[]["fill"]',
|
||||
'RegExp': 'Function("return/"+false+"/")()'
|
||||
}
|
||||
|
||||
def jsunfuck(jsfuckString):
|
||||
|
||||
for key in sorted(MAPPING, key=lambda k: len(MAPPING[k]), reverse=True):
|
||||
if MAPPING.get(key) in jsfuckString:
|
||||
jsfuckString = jsfuckString.replace(MAPPING.get(key), '"{}"'.format(key))
|
||||
|
||||
for key in sorted(SIMPLE, key=lambda k: len(SIMPLE[k]), reverse=True):
|
||||
if SIMPLE.get(key) in jsfuckString:
|
||||
jsfuckString = jsfuckString.replace(SIMPLE.get(key), '{}'.format(key))
|
||||
|
||||
#for key in sorted(CONSTRUCTORS, key=lambda k: len(CONSTRUCTORS[k]), reverse=True):
|
||||
# if CONSTRUCTORS.get(key) in jsfuckString:
|
||||
# jsfuckString = jsfuckString.replace(CONSTRUCTORS.get(key), '{}'.format(key))
|
||||
|
||||
return jsfuckString
|
||||
@@ -1,516 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
"""Death by Captcha HTTP and socket API clients.
|
||||
|
||||
There are two types of Death by Captcha (DBC hereinafter) API: HTTP and
|
||||
socket ones. Both offer the same functionalily, with the socket API
|
||||
sporting faster responses and using way less connections.
|
||||
|
||||
To access the socket API, use SocketClient class; for the HTTP API, use
|
||||
HttpClient class. Both are thread-safe. SocketClient keeps a persistent
|
||||
connection opened and serializes all API requests sent through it, thus
|
||||
it is advised to keep a pool of them if you're script is heavily
|
||||
multithreaded.
|
||||
|
||||
Both SocketClient and HttpClient give you the following methods:
|
||||
|
||||
get_user()
|
||||
Returns your DBC account details as a dict with the following keys:
|
||||
|
||||
"user": your account numeric ID; if login fails, it will be the only
|
||||
item with the value of 0;
|
||||
"rate": your CAPTCHA rate, i.e. how much you will be charged for one
|
||||
solved CAPTCHA in US cents;
|
||||
"balance": your DBC account balance in US cents;
|
||||
"is_banned": flag indicating whether your account is suspended or not.
|
||||
|
||||
get_balance()
|
||||
Returns your DBC account balance in US cents.
|
||||
|
||||
get_captcha(cid)
|
||||
Returns an uploaded CAPTCHA details as a dict with the following keys:
|
||||
|
||||
"captcha": the CAPTCHA numeric ID; if no such CAPTCHAs found, it will
|
||||
be the only item with the value of 0;
|
||||
"text": the CAPTCHA text, if solved, otherwise None;
|
||||
"is_correct": flag indicating whether the CAPTCHA was solved correctly
|
||||
(DBC can detect that in rare cases).
|
||||
|
||||
The only argument `cid` is the CAPTCHA numeric ID.
|
||||
|
||||
get_text(cid)
|
||||
Returns an uploaded CAPTCHA text (None if not solved). The only argument
|
||||
`cid` is the CAPTCHA numeric ID.
|
||||
|
||||
report(cid)
|
||||
Reports an incorrectly solved CAPTCHA. The only argument `cid` is the
|
||||
CAPTCHA numeric ID. Returns True on success, False otherwise.
|
||||
|
||||
upload(captcha)
|
||||
Uploads a CAPTCHA. The only argument `captcha` can be either file-like
|
||||
object (any object with `read` method defined, actually, so StringIO
|
||||
will do), or CAPTCHA image file name. On successul upload you'll get
|
||||
the CAPTCHA details dict (see get_captcha() method).
|
||||
|
||||
NOTE: AT THIS POINT THE UPLOADED CAPTCHA IS NOT SOLVED YET! You have
|
||||
to poll for its status periodically using get_captcha() or get_text()
|
||||
method until the CAPTCHA is solved and you get the text.
|
||||
|
||||
decode(captcha, timeout=DEFAULT_TIMEOUT)
|
||||
A convenient method that uploads a CAPTCHA and polls for its status
|
||||
periodically, but no longer than `timeout` (defaults to 60 seconds).
|
||||
If solved, you'll get the CAPTCHA details dict (see get_captcha()
|
||||
method for details). See upload() method for details on `captcha`
|
||||
argument.
|
||||
|
||||
Visit http://www.deathbycaptcha.com/user/api for updates.
|
||||
|
||||
"""
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import errno
|
||||
import imghdr
|
||||
import random
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
try:
|
||||
from json import read as json_decode, write as json_encode
|
||||
except ImportError:
|
||||
try:
|
||||
from json import loads as json_decode, dumps as json_encode
|
||||
except ImportError:
|
||||
from simplejson import loads as json_decode, dumps as json_encode
|
||||
|
||||
|
||||
# API version and unique software ID
|
||||
API_VERSION = 'DBC/Python v4.6'
|
||||
|
||||
# Default CAPTCHA timeout and decode() polling interval
|
||||
DEFAULT_TIMEOUT = 60
|
||||
DEFAULT_TOKEN_TIMEOUT = 120
|
||||
POLLS_INTERVAL = [1, 1, 2, 3, 2, 2, 3, 2, 2]
|
||||
DFLT_POLL_INTERVAL = 3
|
||||
|
||||
# Base HTTP API url
|
||||
HTTP_BASE_URL = 'http://api.dbcapi.me/api'
|
||||
|
||||
# Preferred HTTP API server's response content type, do not change
|
||||
HTTP_RESPONSE_TYPE = 'application/json'
|
||||
|
||||
# Socket API server's host & ports range
|
||||
SOCKET_HOST = 'api.dbcapi.me'
|
||||
SOCKET_PORTS = range(8123, 8131)
|
||||
|
||||
|
||||
def _load_image(captcha):
|
||||
if hasattr(captcha, 'read'):
|
||||
img = captcha.read()
|
||||
elif type(captcha) == bytearray:
|
||||
img = captcha
|
||||
else:
|
||||
img = ''
|
||||
try:
|
||||
captcha_file = open(captcha, 'rb')
|
||||
except Exception:
|
||||
raise
|
||||
else:
|
||||
img = captcha_file.read()
|
||||
captcha_file.close()
|
||||
if not len(img):
|
||||
raise ValueError('CAPTCHA image is empty')
|
||||
elif imghdr.what(None, img) is None:
|
||||
raise TypeError('Unknown CAPTCHA image type')
|
||||
else:
|
||||
return img
|
||||
|
||||
|
||||
class AccessDeniedException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Client(object):
|
||||
|
||||
"""Death by Captcha API Client."""
|
||||
|
||||
def __init__(self, username, password):
|
||||
self.is_verbose = False
|
||||
self.userpwd = {'username': username, 'password': password}
|
||||
|
||||
def _log(self, cmd, msg=''):
|
||||
if self.is_verbose:
|
||||
print '%d %s %s' % (time.time(), cmd, msg.rstrip())
|
||||
return self
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def connect(self):
|
||||
pass
|
||||
|
||||
def get_user(self):
|
||||
"""Fetch user details -- ID, balance, rate and banned status."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_balance(self):
|
||||
"""Fetch user balance (in US cents)."""
|
||||
return self.get_user().get('balance')
|
||||
|
||||
def get_captcha(self, cid):
|
||||
"""Fetch a CAPTCHA details -- ID, text and correctness flag."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_text(self, cid):
|
||||
"""Fetch a CAPTCHA text."""
|
||||
return self.get_captcha(cid).get('text') or None
|
||||
|
||||
def report(self, cid):
|
||||
"""Report a CAPTCHA as incorrectly solved."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def upload(self, captcha):
|
||||
"""Upload a CAPTCHA.
|
||||
|
||||
Accepts file names and file-like objects. Returns CAPTCHA details
|
||||
dict on success.
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def decode(self, captcha=None, timeout=None, **kwargs):
|
||||
"""
|
||||
Try to solve a CAPTCHA.
|
||||
|
||||
See Client.upload() for arguments details.
|
||||
|
||||
Uploads a CAPTCHA, polls for its status periodically with arbitrary
|
||||
timeout (in seconds), returns CAPTCHA details if (correctly) solved.
|
||||
"""
|
||||
if not timeout:
|
||||
if not captcha:
|
||||
timeout = DEFAULT_TOKEN_TIMEOUT
|
||||
else:
|
||||
timeout = DEFAULT_TIMEOUT
|
||||
|
||||
deadline = time.time() + (max(0, timeout) or DEFAULT_TIMEOUT)
|
||||
uploaded_captcha = self.upload(captcha, **kwargs)
|
||||
if uploaded_captcha:
|
||||
intvl_idx = 0 # POLL_INTERVAL index
|
||||
while deadline > time.time() and not uploaded_captcha.get('text'):
|
||||
intvl, intvl_idx = self._get_poll_interval(intvl_idx)
|
||||
time.sleep(intvl)
|
||||
pulled = self.get_captcha(uploaded_captcha['captcha'])
|
||||
if pulled['captcha'] == uploaded_captcha['captcha']:
|
||||
uploaded_captcha = pulled
|
||||
if uploaded_captcha.get('text') and \
|
||||
uploaded_captcha.get('is_correct'):
|
||||
return uploaded_captcha
|
||||
|
||||
def _get_poll_interval(self, idx):
|
||||
"""Returns poll interval and next index depending on index provided"""
|
||||
|
||||
if len(POLLS_INTERVAL) > idx:
|
||||
intvl = POLLS_INTERVAL[idx]
|
||||
else:
|
||||
intvl = DFLT_POLL_INTERVAL
|
||||
idx += 1
|
||||
|
||||
return intvl, idx
|
||||
|
||||
|
||||
class HttpClient(Client):
|
||||
|
||||
"""Death by Captcha HTTP API client."""
|
||||
|
||||
def __init__(self, *args):
|
||||
Client.__init__(self, *args)
|
||||
self.opener = urllib2.build_opener(urllib2.HTTPRedirectHandler())
|
||||
|
||||
def _call(self, cmd, payload=None, headers=None):
|
||||
if headers is None:
|
||||
headers = {}
|
||||
headers['Accept'] = HTTP_RESPONSE_TYPE
|
||||
headers['User-Agent'] = API_VERSION
|
||||
if hasattr(payload, 'items'):
|
||||
payload = urllib.urlencode(payload)
|
||||
self._log('SEND', '%s %d %s' % (cmd, len(payload), payload))
|
||||
else:
|
||||
self._log('SEND', '%s' % cmd)
|
||||
if payload is not None:
|
||||
headers['Content-Length'] = len(payload)
|
||||
try:
|
||||
response = self.opener.open(urllib2.Request(
|
||||
HTTP_BASE_URL + '/' + cmd.strip('/'),
|
||||
data=payload,
|
||||
headers=headers
|
||||
)).read()
|
||||
except urllib2.HTTPError, err:
|
||||
if 403 == err.code:
|
||||
raise AccessDeniedException('Access denied, please check'
|
||||
' your credentials and/or balance')
|
||||
elif 400 == err.code or 413 == err.code:
|
||||
raise ValueError("CAPTCHA was rejected by the service, check"
|
||||
" if it's a valid image")
|
||||
elif 503 == err.code:
|
||||
raise OverflowError("CAPTCHA was rejected due to service"
|
||||
" overload, try again later")
|
||||
else:
|
||||
raise err
|
||||
else:
|
||||
self._log('RECV', '%d %s' % (len(response), response))
|
||||
try:
|
||||
return json_decode(response)
|
||||
except Exception:
|
||||
raise RuntimeError('Invalid API response')
|
||||
return {}
|
||||
|
||||
def get_user(self):
|
||||
return self._call('user', self.userpwd.copy()) or {'user': 0}
|
||||
|
||||
def get_captcha(self, cid):
|
||||
return self._call('captcha/%d' % cid) or {'captcha': 0}
|
||||
|
||||
def report(self, cid):
|
||||
return not self._call('captcha/%d/report' % cid,
|
||||
self.userpwd.copy()).get('is_correct')
|
||||
|
||||
def upload(self, captcha=None, **kwargs):
|
||||
boundary = binascii.hexlify(os.urandom(16))
|
||||
banner = kwargs.get('banner', '')
|
||||
if banner:
|
||||
kwargs['banner'] = 'base64:' + base64.b64encode(_load_image(banner))
|
||||
body = '\r\n'.join(('\r\n'.join((
|
||||
'--%s' % boundary,
|
||||
'Content-Disposition: form-data; name="%s"' % k,
|
||||
'Content-Type: text/plain',
|
||||
'Content-Length: %d' % len(str(v)),
|
||||
'',
|
||||
str(v)
|
||||
))) for k, v in self.userpwd.items())
|
||||
|
||||
body += '\r\n'.join(('\r\n'.join((
|
||||
'--%s' % boundary,
|
||||
'Content-Disposition: form-data; name="%s"' % k,
|
||||
'Content-Type: text/plain',
|
||||
'Content-Length: %d' % len(str(v)),
|
||||
'',
|
||||
str(v)
|
||||
))) for k, v in kwargs.items())
|
||||
|
||||
if captcha:
|
||||
img = _load_image(captcha)
|
||||
body += '\r\n'.join((
|
||||
'',
|
||||
'--%s' % boundary,
|
||||
'Content-Disposition: form-data; name="captchafile"; '
|
||||
'filename="captcha"',
|
||||
'Content-Type: application/octet-stream',
|
||||
'Content-Length: %d' % len(img),
|
||||
'',
|
||||
img,
|
||||
'--%s--' % boundary,
|
||||
''
|
||||
))
|
||||
|
||||
response = self._call('captcha', body, {
|
||||
'Content-Type': 'multipart/form-data; boundary="%s"' % boundary
|
||||
}) or {}
|
||||
if response.get('captcha'):
|
||||
return response
|
||||
|
||||
|
||||
class SocketClient(Client):
|
||||
|
||||
"""Death by Captcha socket API client."""
|
||||
|
||||
TERMINATOR = '\r\n'
|
||||
|
||||
def __init__(self, *args):
|
||||
Client.__init__(self, *args)
|
||||
self.socket_lock = threading.Lock()
|
||||
self.socket = None
|
||||
|
||||
def close(self):
|
||||
if self.socket:
|
||||
self._log('CLOSE')
|
||||
try:
|
||||
self.socket.shutdown(socket.SHUT_RDWR)
|
||||
except socket.error:
|
||||
pass
|
||||
finally:
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
|
||||
def connect(self):
|
||||
if not self.socket:
|
||||
self._log('CONN')
|
||||
host = (socket.gethostbyname(SOCKET_HOST),
|
||||
random.choice(SOCKET_PORTS))
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.settimeout(0)
|
||||
try:
|
||||
self.socket.connect(host)
|
||||
except socket.error, err:
|
||||
if (err.args[0] not in
|
||||
(errno.EAGAIN, errno.EWOULDBLOCK, errno.EINPROGRESS)):
|
||||
self.close()
|
||||
raise err
|
||||
return self.socket
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def _sendrecv(self, sock, buf):
|
||||
self._log('SEND', buf)
|
||||
fds = [sock]
|
||||
buf += self.TERMINATOR
|
||||
response = ''
|
||||
intvl_idx = 0
|
||||
while True:
|
||||
intvl, intvl_idx = self._get_poll_interval(intvl_idx)
|
||||
rds, wrs, exs = select.select((not buf and fds) or [],
|
||||
(buf and fds) or [],
|
||||
fds,
|
||||
intvl)
|
||||
if exs:
|
||||
raise IOError('select() failed')
|
||||
try:
|
||||
if wrs:
|
||||
while buf:
|
||||
buf = buf[wrs[0].send(buf):]
|
||||
elif rds:
|
||||
while True:
|
||||
s = rds[0].recv(256)
|
||||
if not s:
|
||||
raise IOError('recv(): connection lost')
|
||||
else:
|
||||
response += s
|
||||
except socket.error, err:
|
||||
if (err.args[0] not in
|
||||
(errno.EAGAIN, errno.EWOULDBLOCK, errno.EINPROGRESS)):
|
||||
raise err
|
||||
if response.endswith(self.TERMINATOR):
|
||||
self._log('RECV', response)
|
||||
return response.rstrip(self.TERMINATOR)
|
||||
raise IOError('send/recv timed out')
|
||||
|
||||
def _call(self, cmd, data=None):
|
||||
if data is None:
|
||||
data = {}
|
||||
data['cmd'] = cmd
|
||||
data['version'] = API_VERSION
|
||||
request = json_encode(data)
|
||||
|
||||
response = None
|
||||
for _ in range(2):
|
||||
if not self.socket and cmd != 'login':
|
||||
self._call('login', self.userpwd.copy())
|
||||
self.socket_lock.acquire()
|
||||
try:
|
||||
sock = self.connect()
|
||||
response = self._sendrecv(sock, request)
|
||||
except IOError, err:
|
||||
sys.stderr.write(str(err) + "\n")
|
||||
self.close()
|
||||
except socket.error, err:
|
||||
sys.stderr.write(str(err) + "\n")
|
||||
self.close()
|
||||
raise IOError('Connection refused')
|
||||
else:
|
||||
break
|
||||
finally:
|
||||
self.socket_lock.release()
|
||||
|
||||
if response is None:
|
||||
raise IOError('Connection lost or timed out during API request')
|
||||
|
||||
try:
|
||||
response = json_decode(response)
|
||||
except Exception:
|
||||
raise RuntimeError('Invalid API response')
|
||||
|
||||
if not response.get('error'):
|
||||
return response
|
||||
|
||||
error = response['error']
|
||||
if error in ('not-logged-in', 'invalid-credentials'):
|
||||
raise AccessDeniedException('Access denied, check your credentials')
|
||||
elif 'banned' == error:
|
||||
raise AccessDeniedException('Access denied, account is suspended')
|
||||
elif 'insufficient-funds' == error:
|
||||
raise AccessDeniedException(
|
||||
'CAPTCHA was rejected due to low balance')
|
||||
elif 'invalid-captcha' == error:
|
||||
raise ValueError('CAPTCHA is not a valid image')
|
||||
elif 'service-overload' == error:
|
||||
raise OverflowError(
|
||||
'CAPTCHA was rejected due to service overload, try again later')
|
||||
else:
|
||||
self.socket_lock.acquire()
|
||||
self.close()
|
||||
self.socket_lock.release()
|
||||
raise RuntimeError('API server error occured: %s' % error)
|
||||
|
||||
def get_user(self):
|
||||
return self._call('user') or {'user': 0}
|
||||
|
||||
def get_captcha(self, cid):
|
||||
return self._call('captcha', {'captcha': cid}) or {'captcha': 0}
|
||||
|
||||
def upload(self, captcha=None, **kwargs):
|
||||
data = {}
|
||||
if captcha:
|
||||
data['captcha'] = base64.b64encode(_load_image(captcha))
|
||||
if kwargs:
|
||||
banner = kwargs.get('banner', '')
|
||||
if banner:
|
||||
kwargs['banner'] = base64.b64encode(_load_image(banner))
|
||||
data.update(kwargs)
|
||||
response = self._call('upload', data)
|
||||
if response.get('captcha'):
|
||||
uploaded_captcha = dict(
|
||||
(k, response.get(k))
|
||||
for k in ('captcha', 'text', 'is_correct')
|
||||
)
|
||||
if not uploaded_captcha['text']:
|
||||
uploaded_captcha['text'] = None
|
||||
return uploaded_captcha
|
||||
|
||||
def report(self, cid):
|
||||
return not self._call('report', {'captcha': cid}).get('is_correct')
|
||||
|
||||
|
||||
if '__main__' == __name__:
|
||||
# Put your DBC username & password here:
|
||||
# client = HttpClient(sys.argv[1], sys.argv[2])
|
||||
client = SocketClient(sys.argv[1], sys.argv[2])
|
||||
client.is_verbose = True
|
||||
|
||||
print 'Your balance is %s US cents' % client.get_balance()
|
||||
|
||||
for fn in sys.argv[3:]:
|
||||
try:
|
||||
# Put your CAPTCHA image file name or file-like object, and optional
|
||||
# solving timeout (in seconds) here:
|
||||
captcha = client.decode(fn, DEFAULT_TIMEOUT)
|
||||
except Exception, e:
|
||||
sys.stderr.write('Failed uploading CAPTCHA: %s\n' % (e, ))
|
||||
captcha = None
|
||||
|
||||
if captcha:
|
||||
print 'CAPTCHA %d solved: %s' % \
|
||||
(captcha['captcha'], captcha['text'])
|
||||
|
||||
# Report as incorrectly solved if needed. Make sure the CAPTCHA was
|
||||
# in fact incorrectly solved!
|
||||
# try:
|
||||
# client.report(captcha['captcha'])
|
||||
# except Exception, e:
|
||||
# sys.stderr.write('Failed reporting CAPTCHA: %s\n' % (e, ))
|
||||
@@ -1,75 +0,0 @@
|
||||
# The MIT License
|
||||
#
|
||||
# Copyright 2014, 2015 Piotr Dabkowski
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the 'Software'),
|
||||
# to deal in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so, subject
|
||||
# to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all copies or
|
||||
# substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
""" This module allows you to translate and execute Javascript in pure python.
|
||||
Basically its implementation of ECMAScript 5.1 in pure python.
|
||||
|
||||
Use eval_js method to execute javascript code and get resulting python object (builtin if possible).
|
||||
|
||||
EXAMPLE:
|
||||
>>> import js2py
|
||||
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
||||
>>> add(1, 2) + 3
|
||||
6
|
||||
>>> add('1', 2, 3)
|
||||
u'12'
|
||||
>>> add.constructor
|
||||
function Function() { [python code] }
|
||||
|
||||
|
||||
Or use EvalJs to execute many javascript code fragments under same context - you would be able to get any
|
||||
variable from the context!
|
||||
|
||||
>>> js = js2py.EvalJs()
|
||||
>>> js.execute('var a = 10; function f(x) {return x*x};')
|
||||
>>> js.f(9)
|
||||
81
|
||||
>>> js.a
|
||||
10
|
||||
|
||||
Also you can use its console method to play with interactive javascript console.
|
||||
|
||||
|
||||
Use parse_js to parse (syntax tree is just like in esprima.js) and translate_js to trasnlate JavaScript.
|
||||
|
||||
Finally, you can use pyimport statement from inside JS code to import and use python libraries.
|
||||
|
||||
>>> js2py.eval_js('pyimport urllib; urllib.urlopen("https://www.google.com")')
|
||||
|
||||
NOTE: This module is still not fully finished:
|
||||
|
||||
Date and JSON builtin objects are not implemented
|
||||
Array prototype is not fully finished (will be soon)
|
||||
|
||||
Other than that everything should work fine.
|
||||
|
||||
"""
|
||||
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
__all__ = [
|
||||
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'parse_js',
|
||||
'translate_file', 'run_file', 'disable_pyimport', 'eval_js6',
|
||||
'translate_js6', 'PyJsException', 'get_file_contents',
|
||||
'write_file_contents', 'require'
|
||||
]
|
||||
|
||||
from .base import PyJsException
|
||||
from .evaljs import *
|
||||
from .translators import parse as parse_js
|
||||
from .node_import import require
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
@@ -1,48 +0,0 @@
|
||||
from ..base import *
|
||||
|
||||
|
||||
@Js
|
||||
def Array():
|
||||
if len(arguments) == 0 or len(arguments) > 1:
|
||||
return arguments.to_list()
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber):
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js([])
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
return [a]
|
||||
|
||||
|
||||
Array.create = Array
|
||||
Array.own['length']['value'] = Js(1)
|
||||
|
||||
|
||||
@Js
|
||||
def isArray(arg):
|
||||
return arg.Class == 'Array'
|
||||
|
||||
|
||||
Array.define_own_property('isArray', {
|
||||
'value': isArray,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
ArrayPrototype.define_own_property('constructor', {
|
||||
'value': Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
# todo check everything :)
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def ArrayBuffer():
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber):
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(bytearray([0] * length))
|
||||
return temp
|
||||
return Js(bytearray([0]))
|
||||
|
||||
|
||||
ArrayBuffer.create = ArrayBuffer
|
||||
ArrayBuffer.own['length']['value'] = Js(None)
|
||||
|
||||
ArrayBuffer.define_own_property(
|
||||
'prototype', {
|
||||
'value': ArrayBufferPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
ArrayBufferPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': ArrayBuffer,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
from ..base import *
|
||||
|
||||
BooleanPrototype.define_own_property('constructor', {
|
||||
'value': Boolean,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Boolean.define_own_property(
|
||||
'prototype', {
|
||||
'value': BooleanPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,405 +0,0 @@
|
||||
from ..base import *
|
||||
from .time_helpers import *
|
||||
|
||||
TZ_OFFSET = (time.altzone // 3600)
|
||||
ABS_OFFSET = abs(TZ_OFFSET)
|
||||
TZ_NAME = time.tzname[1]
|
||||
ISO_FORMAT = '%s-%s-%sT%s:%s:%s.%sZ'
|
||||
|
||||
|
||||
@Js
|
||||
def Date(year, month, date, hours, minutes, seconds, ms):
|
||||
return now().to_string()
|
||||
|
||||
|
||||
Date.Class = 'Date'
|
||||
|
||||
|
||||
def now():
|
||||
return PyJsDate(int(time.time() * 1000), prototype=DatePrototype)
|
||||
|
||||
|
||||
@Js
|
||||
def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this
|
||||
args = arguments
|
||||
y = args[0].to_number()
|
||||
m = args[1].to_number()
|
||||
l = len(args)
|
||||
dt = args[2].to_number() if l > 2 else Js(1)
|
||||
h = args[3].to_number() if l > 3 else Js(0)
|
||||
mi = args[4].to_number() if l > 4 else Js(0)
|
||||
sec = args[5].to_number() if l > 5 else Js(0)
|
||||
mili = args[6].to_number() if l > 6 else Js(0)
|
||||
if not y.is_nan() and 0 <= y.value <= 99:
|
||||
y = y + Js(1900)
|
||||
t = TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
|
||||
return PyJsDate(t, prototype=DatePrototype)
|
||||
|
||||
|
||||
@Js
|
||||
def parse(string):
|
||||
return PyJsDate(
|
||||
TimeClip(parse_date(string.to_string().value)),
|
||||
prototype=DatePrototype)
|
||||
|
||||
|
||||
Date.define_own_property('now', {
|
||||
'value': Js(now),
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Date.define_own_property('parse', {
|
||||
'value': parse,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Date.define_own_property('UTC', {
|
||||
'value': UTC,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
|
||||
class PyJsDate(PyJs):
|
||||
Class = 'Date'
|
||||
extensible = True
|
||||
|
||||
def __init__(self, value, prototype=None):
|
||||
self.value = value
|
||||
self.own = {}
|
||||
self.prototype = prototype
|
||||
|
||||
# todo fix this problematic datetime part
|
||||
def to_local_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(
|
||||
UTCToLocal(self.value) // 1000)
|
||||
|
||||
def to_utc_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(self.value // 1000)
|
||||
|
||||
def local_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_local_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
def utc_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_utc_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
|
||||
def parse_date(py_string): # todo support all date string formats
|
||||
try:
|
||||
try:
|
||||
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
except:
|
||||
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%SZ")
|
||||
return MakeDate(
|
||||
MakeDay(Js(dt.year), Js(dt.month - 1), Js(dt.day)),
|
||||
MakeTime(
|
||||
Js(dt.hour), Js(dt.minute), Js(dt.second),
|
||||
Js(dt.microsecond // 1000)))
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not parse date %s - unsupported date format. Currently only supported format is RFC3339 utc. Sorry!'
|
||||
% py_string)
|
||||
|
||||
|
||||
def date_constructor(*args):
|
||||
if len(args) >= 2:
|
||||
return date_constructor2(*args)
|
||||
elif len(args) == 1:
|
||||
return date_constructor1(args[0])
|
||||
else:
|
||||
return date_constructor0()
|
||||
|
||||
|
||||
def date_constructor0():
|
||||
return now()
|
||||
|
||||
|
||||
def date_constructor1(value):
|
||||
v = value.to_primitive()
|
||||
if v._type() == 'String':
|
||||
v = parse_date(v.value)
|
||||
else:
|
||||
v = v.to_int()
|
||||
return PyJsDate(TimeClip(v), prototype=DatePrototype)
|
||||
|
||||
|
||||
def date_constructor2(*args):
|
||||
y = args[0].to_number()
|
||||
m = args[1].to_number()
|
||||
l = len(args)
|
||||
dt = args[2].to_number() if l > 2 else Js(1)
|
||||
h = args[3].to_number() if l > 3 else Js(0)
|
||||
mi = args[4].to_number() if l > 4 else Js(0)
|
||||
sec = args[5].to_number() if l > 5 else Js(0)
|
||||
mili = args[6].to_number() if l > 6 else Js(0)
|
||||
if not y.is_nan() and 0 <= y.value <= 99:
|
||||
y = y + Js(1900)
|
||||
t = TimeClip(
|
||||
LocalToUTC(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili))))
|
||||
return PyJsDate(t, prototype=DatePrototype)
|
||||
|
||||
|
||||
Date.create = date_constructor
|
||||
|
||||
DatePrototype = PyJsDate(float('nan'), prototype=ObjectPrototype)
|
||||
|
||||
|
||||
def check_date(obj):
|
||||
if obj.Class != 'Date':
|
||||
raise MakeError('TypeError', 'this is not a Date object')
|
||||
|
||||
|
||||
class DateProto:
|
||||
def toString():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return 'Invalid Date'
|
||||
offset = (UTCToLocal(this.value) - this.value) // msPerHour
|
||||
return this.local_strftime(
|
||||
'%a %b %d %Y %H:%M:%S GMT') + '%s00 (%s)' % (pad(
|
||||
offset, 2, True), GetTimeZoneName(this.value))
|
||||
|
||||
def toDateString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y')
|
||||
|
||||
def toTimeString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%H:%M:%S')
|
||||
|
||||
def toLocaleString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y %H:%M:%S')
|
||||
|
||||
def toLocaleDateString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y')
|
||||
|
||||
def toLocaleTimeString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%H:%M:%S')
|
||||
|
||||
def valueOf():
|
||||
check_date(this)
|
||||
return this.value
|
||||
|
||||
def getTime():
|
||||
check_date(this)
|
||||
return this.value
|
||||
|
||||
def getFullYear():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return YearFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCFullYear():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return YearFromTime(this.value)
|
||||
|
||||
def getMonth():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MonthFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getDate():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return DateFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMonth():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MonthFromTime(this.value)
|
||||
|
||||
def getUTCDate():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return DateFromTime(this.value)
|
||||
|
||||
def getDay():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return WeekDay(UTCToLocal(this.value))
|
||||
|
||||
def getUTCDay():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return WeekDay(this.value)
|
||||
|
||||
def getHours():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return HourFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCHours():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return HourFromTime(this.value)
|
||||
|
||||
def getMinutes():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MinFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMinutes():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MinFromTime(this.value)
|
||||
|
||||
def getSeconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return SecFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCSeconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return SecFromTime(this.value)
|
||||
|
||||
def getMilliseconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return msFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMilliseconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return msFromTime(this.value)
|
||||
|
||||
def getTimezoneOffset():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return (this.value - UTCToLocal(this.value)) // 60000
|
||||
|
||||
def setTime(time):
|
||||
check_date(this)
|
||||
this.value = TimeClip(time.to_number().to_int())
|
||||
return this.value
|
||||
|
||||
def setMilliseconds(ms):
|
||||
check_date(this)
|
||||
t = UTCToLocal(this.value)
|
||||
tim = MakeTime(
|
||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
||||
u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
|
||||
this.value = u
|
||||
return u
|
||||
|
||||
def setUTCMilliseconds(ms):
|
||||
check_date(this)
|
||||
t = this.value
|
||||
tim = MakeTime(
|
||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
||||
u = TimeClip(MakeDate(Day(t), tim))
|
||||
this.value = u
|
||||
return u
|
||||
|
||||
# todo Complete all setters!
|
||||
|
||||
def toUTCString():
|
||||
check_date(this)
|
||||
return this.utc_strftime('%d %B %Y %H:%M:%S')
|
||||
|
||||
def toISOString():
|
||||
check_date(this)
|
||||
t = this.value
|
||||
year = YearFromTime(t)
|
||||
month, day, hour, minute, second, milli = pad(
|
||||
MonthFromTime(t) + 1), pad(DateFromTime(t)), pad(
|
||||
HourFromTime(t)), pad(MinFromTime(t)), pad(
|
||||
SecFromTime(t)), pad(msFromTime(t))
|
||||
return ISO_FORMAT % (unicode(year) if 0 <= year <= 9999 else pad(
|
||||
year, 6, True), month, day, hour, minute, second, milli)
|
||||
|
||||
def toJSON(key):
|
||||
o = this.to_object()
|
||||
tv = o.to_primitive('Number')
|
||||
if tv.Class == 'Number' and not tv.is_finite():
|
||||
return this.null
|
||||
toISO = o.get('toISOString')
|
||||
if not toISO.is_callable():
|
||||
raise this.MakeError('TypeError', 'toISOString is not callable')
|
||||
return toISO.call(o, ())
|
||||
|
||||
|
||||
def pad(num, n=2, sign=False):
|
||||
'''returns n digit string representation of the num'''
|
||||
s = unicode(abs(num))
|
||||
if len(s) < n:
|
||||
s = '0' * (n - len(s)) + s
|
||||
if not sign:
|
||||
return s
|
||||
if num >= 0:
|
||||
return '+' + s
|
||||
else:
|
||||
return '-' + s
|
||||
|
||||
|
||||
fill_prototype(DatePrototype, DateProto, default_attrs)
|
||||
|
||||
Date.define_own_property(
|
||||
'prototype', {
|
||||
'value': DatePrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
DatePrototype.define_own_property('constructor', {
|
||||
'value': Date,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Float32Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.float32))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.float32))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.float32))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 4 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of Float32Array should be a multiple of 4')
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
if offset % 4 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Start offset of Float32Array should be a multiple of 4')
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int((len(a.obj) - offset) / 4)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.float32, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.float32))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Float32Array.create = Float32Array
|
||||
Float32Array.own['length']['value'] = Js(3)
|
||||
|
||||
Float32Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Float32ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Float32ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Float32Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Float32ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(4),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Float64Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.float64))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.float64))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.float64))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 8 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of Float64Array should be a multiple of 8')
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
if offset % 8 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Start offset of Float64Array should be a multiple of 8')
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int((len(a.obj) - offset) / 8)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.float64, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.float64))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Float64Array.create = Float64Array
|
||||
Float64Array.own['length']['value'] = Js(3)
|
||||
|
||||
Float64Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Float64ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Float64ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Float64Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Float64ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(8),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,52 +0,0 @@
|
||||
from ..base import *
|
||||
try:
|
||||
from ..translators.translator import translate_js
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Function():
|
||||
# convert arguments to python list of strings
|
||||
a = [e.to_string().value for e in arguments.to_list()]
|
||||
body = ';'
|
||||
args = ()
|
||||
if len(a):
|
||||
body = '%s;' % a[-1]
|
||||
args = a[:-1]
|
||||
# translate this function to js inline function
|
||||
js_func = '(function (%s) {%s})' % (','.join(args), body)
|
||||
# now translate js inline to python function
|
||||
py_func = translate_js(js_func, '')
|
||||
# add set func scope to global scope
|
||||
# a but messy solution but works :)
|
||||
globals()['var'] = PyJs.GlobalObject
|
||||
# define py function and return it
|
||||
temp = executor(py_func, globals())
|
||||
temp.source = '{%s}' % body
|
||||
temp.func_name = 'anonymous'
|
||||
return temp
|
||||
|
||||
|
||||
def executor(f, glob):
|
||||
exec (f, globals())
|
||||
return globals()['PyJs_anonymous_0_']
|
||||
|
||||
|
||||
#new statement simply calls Function
|
||||
Function.create = Function
|
||||
|
||||
#set constructor property inside FunctionPrototype
|
||||
|
||||
fill_in_props(FunctionPrototype, {'constructor': Function}, default_attrs)
|
||||
|
||||
#attach prototype to Function constructor
|
||||
Function.define_own_property(
|
||||
'prototype', {
|
||||
'value': FunctionPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
#Fix Function length (its 0 and should be 1)
|
||||
Function.own['length']['value'] = Js(1)
|
||||
@@ -1,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Int16Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.int16))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.int16))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.int16))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 2 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of Int16Array should be a multiple of 2')
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
if offset % 2 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Start offset of Int16Array should be a multiple of 2')
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int((len(a.obj) - offset) / 2)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.int16, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.int16))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Int16Array.create = Int16Array
|
||||
Int16Array.own['length']['value'] = Js(3)
|
||||
|
||||
Int16Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Int16ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Int16ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Int16Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Int16ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(2),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Int32Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.int32))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.int32))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.int32))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 4 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of Int32Array should be a multiple of 4')
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
if offset % 4 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Start offset of Int32Array should be a multiple of 4')
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int((len(a.obj) - offset) / 4)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.int32, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.int32))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Int32Array.create = Int32Array
|
||||
Int32Array.own['length']['value'] = Js(3)
|
||||
|
||||
Int32Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Int32ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Int32ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Int32Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Int32ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(4),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,79 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Int8Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.int8))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
elif isinstance(a, PyJsString): # object (string)
|
||||
temp = Js(numpy.array(list(a.value), dtype=numpy.int8))
|
||||
temp.put('length', Js(len(list(a.value))))
|
||||
return temp
|
||||
elif isinstance(a, PyJsArray) or isinstance(a, TypedArray) or isinstance(
|
||||
a, PyJsArrayBuffer): # object (Array, TypedArray)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.int8))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int(len(a.obj) - offset)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.int8, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.int8))
|
||||
temp.put('length', Js(0))
|
||||
return temp
|
||||
|
||||
|
||||
Int8Array.create = Int8Array
|
||||
Int8Array.own['length']['value'] = Js(3)
|
||||
|
||||
Int8Array.define_own_property(
|
||||
'prototype', {
|
||||
'value': Int8ArrayPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
Int8ArrayPrototype.define_own_property(
|
||||
'constructor', {
|
||||
'value': Int8Array,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Int8ArrayPrototype.define_own_property('BYTES_PER_ELEMENT', {
|
||||
'value': Js(1),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
@@ -1,157 +0,0 @@
|
||||
from ..base import *
|
||||
import math
|
||||
import random
|
||||
|
||||
Math = PyJsObject(prototype=ObjectPrototype)
|
||||
Math.Class = 'Math'
|
||||
|
||||
CONSTANTS = {
|
||||
'E': 2.7182818284590452354,
|
||||
'LN10': 2.302585092994046,
|
||||
'LN2': 0.6931471805599453,
|
||||
'LOG2E': 1.4426950408889634,
|
||||
'LOG10E': 0.4342944819032518,
|
||||
'PI': 3.1415926535897932,
|
||||
'SQRT1_2': 0.7071067811865476,
|
||||
'SQRT2': 1.4142135623730951
|
||||
}
|
||||
|
||||
for constant, value in CONSTANTS.items():
|
||||
Math.define_own_property(
|
||||
constant, {
|
||||
'value': Js(value),
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
|
||||
class MathFunctions:
|
||||
def abs(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return abs(a)
|
||||
|
||||
def acos(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.acos(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def asin(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.asin(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def atan(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.atan(a)
|
||||
|
||||
def atan2(y, x):
|
||||
a = x.to_number().value
|
||||
b = y.to_number().value
|
||||
if a != a or b != b: # it must be a nan
|
||||
return NaN
|
||||
return math.atan2(b, a)
|
||||
|
||||
def ceil(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.ceil(a)
|
||||
|
||||
def floor(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.floor(a)
|
||||
|
||||
def round(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return round(a)
|
||||
|
||||
def sin(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.sin(a)
|
||||
|
||||
def cos(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.cos(a)
|
||||
|
||||
def tan(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.tan(a)
|
||||
|
||||
def log(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.log(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def exp(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.exp(a)
|
||||
|
||||
def pow(x, y):
|
||||
a = x.to_number().value
|
||||
b = y.to_number().value
|
||||
if a != a or b != b: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return a**b
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def sqrt(x):
|
||||
a = x.to_number().value
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return a**0.5
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def min():
|
||||
if not len(arguments):
|
||||
return Infinity
|
||||
lis = tuple(e.to_number().value for e in arguments.to_list())
|
||||
if any(e != e for e in lis): # we dont want NaNs
|
||||
return NaN
|
||||
return min(*lis)
|
||||
|
||||
def max():
|
||||
if not len(arguments):
|
||||
return -Infinity
|
||||
lis = tuple(e.to_number().value for e in arguments.to_list())
|
||||
if any(e != e for e in lis): # we dont want NaNs
|
||||
return NaN
|
||||
return max(*lis)
|
||||
|
||||
def random():
|
||||
return random.random()
|
||||
|
||||
|
||||
fill_prototype(Math, MathFunctions, default_attrs)
|
||||
@@ -1,23 +0,0 @@
|
||||
from ..base import *
|
||||
|
||||
CONSTS = {
|
||||
'prototype': NumberPrototype,
|
||||
'MAX_VALUE': 1.7976931348623157e308,
|
||||
'MIN_VALUE': 5.0e-324,
|
||||
'NaN': NaN,
|
||||
'NEGATIVE_INFINITY': float('-inf'),
|
||||
'POSITIVE_INFINITY': float('inf')
|
||||
}
|
||||
|
||||
fill_in_props(Number, CONSTS, {
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
NumberPrototype.define_own_property('constructor', {
|
||||
'value': Number,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,198 +0,0 @@
|
||||
from ..base import *
|
||||
import six
|
||||
|
||||
#todo Double check everything is OK
|
||||
|
||||
|
||||
@Js
|
||||
def Object():
|
||||
val = arguments.get('0')
|
||||
if val.is_null() or val.is_undefined():
|
||||
return PyJsObject(prototype=ObjectPrototype)
|
||||
return val.to_object()
|
||||
|
||||
|
||||
@Js
|
||||
def object_constructor():
|
||||
if len(arguments):
|
||||
val = arguments.get('0')
|
||||
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)
|
||||
|
||||
|
||||
class ObjectMethods:
|
||||
def getPrototypeOf(obj):
|
||||
if not obj.is_object():
|
||||
raise MakeError('TypeError',
|
||||
'Object.getPrototypeOf called on non-object')
|
||||
return null if obj.prototype is None else obj.prototype
|
||||
|
||||
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
|
||||
|
||||
def getOwnPropertyNames(obj):
|
||||
if not obj.is_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')
|
||||
temp = PyJsObject(prototype=(None if obj.is_null() else obj))
|
||||
if len(arguments) > 1 and not arguments[1].is_undefined():
|
||||
if six.PY2:
|
||||
ObjectMethods.defineProperties.__func__(temp, arguments[1])
|
||||
else:
|
||||
ObjectMethods.defineProperties(temp, arguments[1])
|
||||
return temp
|
||||
|
||||
def defineProperty(obj, prop, attrs):
|
||||
if not obj.is_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)
|
||||
return obj
|
||||
|
||||
def defineProperties(obj, properties):
|
||||
if not obj.is_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)
|
||||
return obj
|
||||
|
||||
def seal(obj):
|
||||
if not obj.is_object():
|
||||
raise MakeError('TypeError', 'Object.seal called on non-object')
|
||||
for desc in obj.own.values():
|
||||
desc['configurable'] = False
|
||||
obj.extensible = False
|
||||
return obj
|
||||
|
||||
def freeze(obj):
|
||||
if not obj.is_object():
|
||||
raise MakeError('TypeError', 'Object.freeze called on non-object')
|
||||
for desc in obj.own.values():
|
||||
desc['configurable'] = False
|
||||
if is_data_descriptor(desc):
|
||||
desc['writable'] = False
|
||||
obj.extensible = False
|
||||
return obj
|
||||
|
||||
def preventExtensions(obj):
|
||||
if not obj.is_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')
|
||||
if obj.extensible:
|
||||
return False
|
||||
for desc in obj.own.values():
|
||||
if desc['configurable']:
|
||||
return False
|
||||
return True
|
||||
|
||||
def isFrozen(obj):
|
||||
if not obj.is_object():
|
||||
raise MakeError('TypeError',
|
||||
'Object.isFrozen called on non-object')
|
||||
if obj.extensible:
|
||||
return False
|
||||
for desc in obj.own.values():
|
||||
if desc['configurable']:
|
||||
return False
|
||||
if is_data_descriptor(desc) and desc['writable']:
|
||||
return False
|
||||
return True
|
||||
|
||||
def isExtensible(obj):
|
||||
if not obj.is_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')]
|
||||
|
||||
|
||||
# add methods attached to Object constructor
|
||||
fill_prototype(Object, ObjectMethods, default_attrs)
|
||||
# add constructor to prototype
|
||||
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
|
||||
})
|
||||
|
||||
# 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')
|
||||
desc = {}
|
||||
if obj.has_property('enumerable'):
|
||||
desc['enumerable'] = obj.get('enumerable').to_boolean().value
|
||||
if obj.has_property('configurable'):
|
||||
desc['configurable'] = obj.get('configurable').to_boolean().value
|
||||
if obj.has_property('value'):
|
||||
desc['value'] = obj.get('value')
|
||||
if obj.has_property('writable'):
|
||||
desc['writable'] = obj.get('writable').to_boolean().value
|
||||
if obj.has_property('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)')
|
||||
desc['get'] = cand
|
||||
if obj.has_property('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)')
|
||||
desc['set'] = cand
|
||||
if ('get' in desc or 'set' in desc) and ('value' in desc
|
||||
or 'writable' in desc):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Invalid property. A property cannot both have accessors and be writable or have a value.'
|
||||
)
|
||||
return desc
|
||||
@@ -1,16 +0,0 @@
|
||||
from ..base import *
|
||||
|
||||
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
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
from ..base import *
|
||||
# python 3 support
|
||||
import six
|
||||
if six.PY3:
|
||||
unichr = chr
|
||||
|
||||
|
||||
@Js
|
||||
def fromCharCode():
|
||||
args = arguments.to_list()
|
||||
res = u''
|
||||
for e in args:
|
||||
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(
|
||||
'prototype', {
|
||||
'value': StringPrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
StringPrototype.define_own_property('constructor', {
|
||||
'value': String,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,87 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Uint16Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.uint16))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
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)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.uint16))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(a.obj) % 2 != 0:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Byte length of 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')
|
||||
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)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.uint16))
|
||||
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
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
@@ -1,95 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Uint32Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.uint32))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
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)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
else:
|
||||
offset = 0
|
||||
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))
|
||||
temp.put('length', Js(length))
|
||||
return temp
|
||||
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')
|
||||
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')
|
||||
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))
|
||||
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
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
@@ -1,79 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Uint8Array():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.uint8))
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
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)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.uint8))
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int(len(a.obj) - offset)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.uint8, count=length, offset=offset)
|
||||
temp = Js(array)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.uint8))
|
||||
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
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
@@ -1,79 +0,0 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
from ..base import *
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Uint8ClampedArray():
|
||||
TypedArray = (PyJsInt8Array, PyJsUint8Array, PyJsUint8ClampedArray,
|
||||
PyJsInt16Array, PyJsUint16Array, PyJsInt32Array,
|
||||
PyJsUint32Array, PyJsFloat32Array, PyJsFloat64Array)
|
||||
a = arguments[0]
|
||||
if isinstance(a, PyJsNumber): # length
|
||||
length = a.to_uint32()
|
||||
if length != a.value:
|
||||
raise MakeError('RangeError', 'Invalid array length')
|
||||
temp = Js(numpy.full(length, 0, dtype=numpy.uint8), Clamped=True)
|
||||
temp.put('length', a)
|
||||
return temp
|
||||
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)
|
||||
array = a.to_list()
|
||||
array = [(int(item.value) if item.value != None else 0)
|
||||
for item in array]
|
||||
temp = Js(numpy.array(array, dtype=numpy.uint8), Clamped=True)
|
||||
temp.put('length', Js(len(array)))
|
||||
return temp
|
||||
elif isinstance(a, PyObjectWrapper): # object (ArrayBuffer, etc)
|
||||
if len(arguments) > 1:
|
||||
offset = int(arguments[1].value)
|
||||
else:
|
||||
offset = 0
|
||||
if len(arguments) > 2:
|
||||
length = int(arguments[2].value)
|
||||
else:
|
||||
length = int(len(a.obj) - offset)
|
||||
array = numpy.frombuffer(
|
||||
a.obj, dtype=numpy.uint8, count=length, offset=offset)
|
||||
temp = Js(array, Clamped=True)
|
||||
temp.put('length', Js(length))
|
||||
temp.buff = array
|
||||
return temp
|
||||
temp = Js(numpy.full(0, 0, dtype=numpy.uint8), Clamped=True)
|
||||
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
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
@@ -1,207 +0,0 @@
|
||||
# NOTE: t must be INT!!!
|
||||
import time
|
||||
import datetime
|
||||
import warnings
|
||||
|
||||
try:
|
||||
from tzlocal import get_localzone
|
||||
LOCAL_ZONE = get_localzone()
|
||||
except: # except all problems...
|
||||
warnings.warn(
|
||||
'Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time'
|
||||
)
|
||||
|
||||
class LOCAL_ZONE:
|
||||
@staticmethod
|
||||
def dst(*args):
|
||||
return 1
|
||||
|
||||
|
||||
from js2py.base import MakeError
|
||||
CUM = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
|
||||
msPerDay = 86400000
|
||||
msPerYear = int(86400000 * 365.242)
|
||||
msPerSecond = 1000
|
||||
msPerMinute = 60000
|
||||
msPerHour = 3600000
|
||||
HoursPerDay = 24
|
||||
MinutesPerHour = 60
|
||||
SecondsPerMinute = 60
|
||||
NaN = float('nan')
|
||||
LocalTZA = -time.timezone * msPerSecond
|
||||
|
||||
|
||||
def DaylightSavingTA(t):
|
||||
if t is NaN:
|
||||
return t
|
||||
try:
|
||||
return int(
|
||||
LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(
|
||||
t // 1000)).seconds) * 1000
|
||||
except:
|
||||
warnings.warn(
|
||||
'Invalid datetime date, assumed DST time, may be inaccurate...',
|
||||
Warning)
|
||||
return 1
|
||||
#raise MakeError('TypeError', 'date not supported by python.datetime. I will solve it in future versions')
|
||||
|
||||
|
||||
def GetTimeZoneName(t):
|
||||
return time.tzname[DaylightSavingTA(t) > 0]
|
||||
|
||||
|
||||
def LocalToUTC(t):
|
||||
return t - LocalTZA - DaylightSavingTA(t - LocalTZA)
|
||||
|
||||
|
||||
def UTCToLocal(t):
|
||||
return t + LocalTZA + DaylightSavingTA(t)
|
||||
|
||||
|
||||
def Day(t):
|
||||
return t // 86400000
|
||||
|
||||
|
||||
def TimeWithinDay(t):
|
||||
return t % 86400000
|
||||
|
||||
|
||||
def DaysInYear(y):
|
||||
if y % 4:
|
||||
return 365
|
||||
elif y % 100:
|
||||
return 366
|
||||
elif y % 400:
|
||||
return 365
|
||||
else:
|
||||
return 366
|
||||
|
||||
|
||||
def DayFromYear(y):
|
||||
return 365 * (y - 1970) + (y - 1969) // 4 - (y - 1901) // 100 + (
|
||||
y - 1601) // 400
|
||||
|
||||
|
||||
def TimeFromYear(y):
|
||||
return 86400000 * DayFromYear(y)
|
||||
|
||||
|
||||
def YearFromTime(t):
|
||||
guess = 1970 - t // 31556908800 # msPerYear
|
||||
gt = TimeFromYear(guess)
|
||||
if gt <= t:
|
||||
while gt <= t:
|
||||
guess += 1
|
||||
gt = TimeFromYear(guess)
|
||||
return guess - 1
|
||||
else:
|
||||
while gt > t:
|
||||
guess -= 1
|
||||
gt = TimeFromYear(guess)
|
||||
return guess
|
||||
|
||||
|
||||
def DayWithinYear(t):
|
||||
return Day(t) - DayFromYear(YearFromTime(t))
|
||||
|
||||
|
||||
def InLeapYear(t):
|
||||
y = YearFromTime(t)
|
||||
if y % 4:
|
||||
return 0
|
||||
elif y % 100:
|
||||
return 1
|
||||
elif y % 400:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
def MonthFromTime(t):
|
||||
day = DayWithinYear(t)
|
||||
leap = InLeapYear(t)
|
||||
if day < 31:
|
||||
return 0
|
||||
day -= leap
|
||||
if day < 59:
|
||||
return 1
|
||||
elif day < 90:
|
||||
return 2
|
||||
elif day < 120:
|
||||
return 3
|
||||
elif day < 151:
|
||||
return 4
|
||||
elif day < 181:
|
||||
return 5
|
||||
elif day < 212:
|
||||
return 6
|
||||
elif day < 243:
|
||||
return 7
|
||||
elif day < 273:
|
||||
return 8
|
||||
elif day < 304:
|
||||
return 9
|
||||
elif day < 334:
|
||||
return 10
|
||||
else:
|
||||
return 11
|
||||
|
||||
|
||||
def DateFromTime(t):
|
||||
mon = MonthFromTime(t)
|
||||
day = DayWithinYear(t)
|
||||
return day - CUM[mon] - (1 if InLeapYear(t) and mon >= 2 else 0) + 1
|
||||
|
||||
|
||||
def WeekDay(t):
|
||||
# 0 == sunday
|
||||
return (Day(t) + 4) % 7
|
||||
|
||||
|
||||
def msFromTime(t):
|
||||
return t % 1000
|
||||
|
||||
|
||||
def SecFromTime(t):
|
||||
return (t // 1000) % 60
|
||||
|
||||
|
||||
def MinFromTime(t):
|
||||
return (t // 60000) % 60
|
||||
|
||||
|
||||
def HourFromTime(t):
|
||||
return (t // 3600000) % 24
|
||||
|
||||
|
||||
def MakeTime(hour, Min, sec, ms):
|
||||
# takes PyJs objects and returns t
|
||||
if not (hour.is_finite() and Min.is_finite() and sec.is_finite()
|
||||
and ms.is_finite()):
|
||||
return NaN
|
||||
h, m, s, milli = hour.to_int(), Min.to_int(), sec.to_int(), ms.to_int()
|
||||
return h * 3600000 + m * 60000 + s * 1000 + milli
|
||||
|
||||
|
||||
def MakeDay(year, month, date):
|
||||
# takes PyJs objects and returns t
|
||||
if not (year.is_finite() and month.is_finite() and date.is_finite()):
|
||||
return NaN
|
||||
y, m, dt = year.to_int(), month.to_int(), date.to_int()
|
||||
y += m // 12
|
||||
mn = m % 12
|
||||
d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y) == 366
|
||||
and mn >= 2 else 0)
|
||||
return d # ms per day
|
||||
|
||||
|
||||
def MakeDate(day, time):
|
||||
return 86400000 * day + time
|
||||
|
||||
|
||||
def TimeClip(t):
|
||||
if t != t or abs(t) == float('inf'):
|
||||
return NaN
|
||||
if abs(t) > 8.64 * 10**15:
|
||||
return NaN
|
||||
return int(t)
|
||||
@@ -1,41 +0,0 @@
|
||||
INITIALISED = False
|
||||
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!'
|
||||
)
|
||||
|
||||
from .babel import babel as _babel
|
||||
babel = _babel.Object.babel
|
||||
babelPresetEs2015 = _babel.Object.babelPresetEs2015
|
||||
|
||||
# very weird hack. Somehow this helps babel to initialise properly!
|
||||
try:
|
||||
babel.transform('warmup', {'presets': {}})
|
||||
signal.alarm(2)
|
||||
|
||||
def kill_it(a, b):
|
||||
raise KeyboardInterrupt('Better work next time!')
|
||||
|
||||
signal.signal(signal.SIGALRM, kill_it)
|
||||
babel.transform('stuckInALoop', {
|
||||
'presets': babelPresetEs2015
|
||||
}).code
|
||||
for n in range(3):
|
||||
time.sleep(1)
|
||||
except:
|
||||
print("Initialised babel!")
|
||||
INITIALISED = True
|
||||
return babel.transform(code, {'presets': babelPresetEs2015}).code
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(js6_to_js5('obj={}; obj.x = function() {return () => this}'))
|
||||
print()
|
||||
print(js6_to_js5('const a = 1;'))
|
||||
@@ -1,6 +0,0 @@
|
||||
// run buildBabel in this folder to convert this code to python!
|
||||
var babel = require("babel-core");
|
||||
var babelPresetEs2015 = require("babel-preset-es2015");
|
||||
|
||||
Object.babelPresetEs2015 = babelPresetEs2015;
|
||||
Object.babel = babel;
|
||||
File diff suppressed because one or more lines are too long
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
# install all the dependencies and pack everything into one file babel_bundle.js (converted to es2015).
|
||||
npm install babel-core babel-cli babel-preset-es2015 browserify
|
||||
browserify babel.js -o babel_bundle.js -t [ babelify --presets [ es2015 ] ]
|
||||
|
||||
# translate babel_bundle.js using js2py -> generates babel.py
|
||||
echo "Generating babel.py..."
|
||||
python -c "import js2py;js2py.translate_file('babel_bundle.js', 'babel.py');"
|
||||
rm babel_bundle.js
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
# coding=utf-8
|
||||
from .translators import translate_js, DEFAULT_HEADER
|
||||
from .es6 import js6_to_js5
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import six
|
||||
import os
|
||||
import hashlib
|
||||
import codecs
|
||||
|
||||
__all__ = [
|
||||
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'translate_file',
|
||||
'eval_js6', 'translate_js6', 'run_file', 'disable_pyimport',
|
||||
'get_file_contents', 'write_file_contents'
|
||||
]
|
||||
DEBUG = False
|
||||
|
||||
|
||||
def disable_pyimport():
|
||||
import pyjsparser.parser
|
||||
pyjsparser.parser.ENABLE_PYIMPORT = False
|
||||
|
||||
|
||||
def path_as_local(path):
|
||||
if os.path.isabs(path):
|
||||
return path
|
||||
# relative to cwd
|
||||
return os.path.join(os.getcwd(), path)
|
||||
|
||||
|
||||
def import_js(path, lib_name, globals):
|
||||
"""Imports from javascript source file.
|
||||
globals is your globals()"""
|
||||
with codecs.open(path_as_local(path), "r", "utf-8") as f:
|
||||
js = f.read()
|
||||
e = EvalJs()
|
||||
e.execute(js)
|
||||
var = e.context['var']
|
||||
globals[lib_name] = var.to_python()
|
||||
|
||||
|
||||
def get_file_contents(path_or_file):
|
||||
if hasattr(path_or_file, 'read'):
|
||||
js = path_or_file.read()
|
||||
else:
|
||||
with codecs.open(path_as_local(path_or_file), "r", "utf-8") as f:
|
||||
js = f.read()
|
||||
return js
|
||||
|
||||
|
||||
def write_file_contents(path_or_file, contents):
|
||||
if hasattr(path_or_file, 'write'):
|
||||
path_or_file.write(contents)
|
||||
else:
|
||||
with open(path_as_local(path_or_file), 'w') as f:
|
||||
f.write(contents)
|
||||
|
||||
|
||||
def translate_file(input_path, output_path):
|
||||
'''
|
||||
Translates input JS file to python and saves the it to the output path.
|
||||
It appends some convenience code at the end so that it is easy to import JS objects.
|
||||
|
||||
For example we have a file 'example.js' with: var a = function(x) {return x}
|
||||
translate_file('example.js', 'example.py')
|
||||
|
||||
Now example.py can be easily importend and used:
|
||||
>>> from example import example
|
||||
>>> example.a(30)
|
||||
30
|
||||
'''
|
||||
js = get_file_contents(input_path)
|
||||
|
||||
py_code = translate_js(js)
|
||||
lib_name = os.path.basename(output_path).split('.')[0]
|
||||
head = '__all__ = [%s]\n\n# Don\'t look below, you will not understand this Python code :) I don\'t.\n\n' % repr(
|
||||
lib_name)
|
||||
tail = '\n\n# Add lib to the module scope\n%s = var.to_python()' % lib_name
|
||||
out = head + py_code + tail
|
||||
write_file_contents(output_path, out)
|
||||
|
||||
|
||||
def run_file(path_or_file, context=None):
|
||||
''' Context must be EvalJS object. Runs given path as a JS program. Returns (eval_value, context).
|
||||
'''
|
||||
if context is None:
|
||||
context = EvalJs()
|
||||
if not isinstance(context, EvalJs):
|
||||
raise TypeError('context must be the instance of EvalJs')
|
||||
eval_value = context.eval(get_file_contents(path_or_file))
|
||||
return eval_value, context
|
||||
|
||||
|
||||
def eval_js(js):
|
||||
"""Just like javascript eval. Translates javascript to python,
|
||||
executes and returns python object.
|
||||
js is javascript source code
|
||||
|
||||
EXAMPLE:
|
||||
>>> import js2py
|
||||
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
||||
>>> add(1, 2) + 3
|
||||
6
|
||||
>>> add('1', 2, 3)
|
||||
u'12'
|
||||
>>> add.constructor
|
||||
function Function() { [python code] }
|
||||
|
||||
NOTE: For Js Number, String, Boolean and other base types returns appropriate python BUILTIN type.
|
||||
For Js functions and objects, returns Python wrapper - basically behaves like normal python object.
|
||||
If you really want to convert object to python dict you can use to_dict method.
|
||||
"""
|
||||
e = EvalJs()
|
||||
return e.eval(js)
|
||||
|
||||
|
||||
def eval_js6(js):
|
||||
return eval_js(js6_to_js5(js))
|
||||
|
||||
|
||||
def translate_js6(js):
|
||||
return translate_js(js6_to_js5(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)
|
||||
81
|
||||
>>> js.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
|
||||
30
|
||||
|
||||
You can run interactive javascript console with console method!"""
|
||||
|
||||
def __init__(self, context={}):
|
||||
self.__dict__['_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')
|
||||
for k, v in six.iteritems(context):
|
||||
setattr(self._var, k, v)
|
||||
|
||||
def execute(self, js=None, use_compilation_plan=False):
|
||||
"""executes javascript js in current context
|
||||
|
||||
During initial execute() the converted js is cached for re-use. That means next time you
|
||||
run the same javascript snippet you save many instructions needed to parse and convert the
|
||||
js code to python code.
|
||||
|
||||
This cache causes minor overhead (a cache dicts is updated) but the Js=>Py conversion process
|
||||
is typically expensive compared to actually running the generated python code.
|
||||
|
||||
Note that the cache is just a dict, it has no expiration or cleanup so when running this
|
||||
in automated situations with vast amounts of snippets it might increase memory usage.
|
||||
"""
|
||||
try:
|
||||
cache = self.__dict__['cache']
|
||||
except KeyError:
|
||||
cache = self.__dict__['cache'] = {}
|
||||
hashkey = hashlib.md5(js.encode('utf-8')).digest()
|
||||
try:
|
||||
compiled = cache[hashkey]
|
||||
except KeyError:
|
||||
code = translate_js(
|
||||
js, '', use_compilation_plan=use_compilation_plan)
|
||||
compiled = cache[hashkey] = compile(code, '<EvalJS snippet>',
|
||||
'exec')
|
||||
exec (compiled, self._context)
|
||||
|
||||
def eval(self, expression, use_compilation_plan=False):
|
||||
"""evaluates expression in current context and returns its value"""
|
||||
code = 'PyJsEvalResult = eval(%s)' % json.dumps(expression)
|
||||
self.execute(code, use_compilation_plan=use_compilation_plan)
|
||||
return self['PyJsEvalResult']
|
||||
|
||||
def execute_debug(self, js):
|
||||
"""executes javascript js in current context
|
||||
as opposed to the (faster) self.execute method, you can use your regular debugger
|
||||
to set breakpoints and inspect the generated python code
|
||||
"""
|
||||
code = translate_js(js, '')
|
||||
# make sure you have a temp folder:
|
||||
filename = 'temp' + os.sep + '_' + hashlib.md5(
|
||||
code.encode("utf-8")).hexdigest() + '.py'
|
||||
try:
|
||||
with open(filename, mode='w') as f:
|
||||
f.write(code)
|
||||
with open(filename, "r") as f:
|
||||
pyCode = compile(f.read(), filename, 'exec')
|
||||
exec(pyCode, self._context)
|
||||
|
||||
except Exception as err:
|
||||
raise err
|
||||
finally:
|
||||
os.remove(filename)
|
||||
try:
|
||||
os.remove(filename + 'c')
|
||||
except:
|
||||
pass
|
||||
|
||||
def eval_debug(self, expression):
|
||||
"""evaluates expression in current context and returns its value
|
||||
as opposed to the (faster) self.execute method, you can use your regular debugger
|
||||
to set breakpoints and inspect the generated python code
|
||||
"""
|
||||
code = 'PyJsEvalResult = eval(%s)' % json.dumps(expression)
|
||||
self.execute_debug(code)
|
||||
return self['PyJsEvalResult']
|
||||
|
||||
def __getattr__(self, var):
|
||||
return getattr(self._var, var)
|
||||
|
||||
def __getitem__(self, var):
|
||||
return getattr(self._var, var)
|
||||
|
||||
def __setattr__(self, var, val):
|
||||
return setattr(self._var, var, val)
|
||||
|
||||
def __setitem__(self, var, val):
|
||||
return setattr(self._var, var, val)
|
||||
|
||||
def console(self):
|
||||
"""starts to interact (starts interactive console) Something like code.InteractiveConsole"""
|
||||
while True:
|
||||
if six.PY2:
|
||||
code = raw_input('>>> ')
|
||||
else:
|
||||
code = input('>>>')
|
||||
try:
|
||||
print(self.eval(code))
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except Exception as e:
|
||||
import traceback
|
||||
if DEBUG:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
else:
|
||||
sys.stderr.write('EXCEPTION: ' + str(e) + '\n')
|
||||
time.sleep(0.01)
|
||||
|
||||
|
||||
#print x
|
||||
|
||||
if __name__ == '__main__':
|
||||
#with open('C:\Users\Piotrek\Desktop\esprima.js', 'rb') as f:
|
||||
# x = f.read()
|
||||
e = EvalJs()
|
||||
e.execute('square(x)')
|
||||
#e.execute(x)
|
||||
e.console()
|
||||
@@ -1,15 +0,0 @@
|
||||
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)
|
||||
@@ -1,51 +0,0 @@
|
||||
from ..base import *
|
||||
import inspect
|
||||
try:
|
||||
from js2py.translators.translator import translate_js
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@Js
|
||||
def Eval(code):
|
||||
local_scope = inspect.stack()[3][0].f_locals['var']
|
||||
global_scope = this.GlobalObject
|
||||
# todo fix scope - we have to behave differently if called through variable other than eval
|
||||
# we will use local scope (default)
|
||||
globals()['var'] = local_scope
|
||||
try:
|
||||
py_code = translate_js(code.to_string().value, '')
|
||||
except SyntaxError as syn_err:
|
||||
raise MakeError('SyntaxError', str(syn_err))
|
||||
lines = py_code.split('\n')
|
||||
# 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]
|
||||
if line.strip():
|
||||
if line.startswith(' '):
|
||||
break
|
||||
elif line.strip() == 'pass':
|
||||
continue
|
||||
elif any(
|
||||
line.startswith(e)
|
||||
for e in ['return ', 'continue ', 'break', 'raise ']):
|
||||
break
|
||||
else:
|
||||
has_return = True
|
||||
cand = 'EVAL_RESULT = (%s)\n' % line
|
||||
try:
|
||||
compile(cand, '', 'exec')
|
||||
except SyntaxError:
|
||||
break
|
||||
lines[len(lines) - n - 1] = cand
|
||||
py_code = '\n'.join(lines)
|
||||
break
|
||||
#print py_code
|
||||
executor(py_code)
|
||||
if has_return:
|
||||
return globals()['EVAL_RESULT']
|
||||
|
||||
|
||||
def executor(code):
|
||||
exec (code, globals())
|
||||
@@ -1,176 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@Js
|
||||
def parseInt(string, radix):
|
||||
string = string.to_string().value.lstrip()
|
||||
sign = 1
|
||||
if string and string[0] in ('+', '-'):
|
||||
if string[0] == '-':
|
||||
sign = -1
|
||||
string = string[1:]
|
||||
r = radix.to_int32()
|
||||
strip_prefix = True
|
||||
if r:
|
||||
if r < 2 or r > 36:
|
||||
return NaN
|
||||
if r != 16:
|
||||
strip_prefix = False
|
||||
else:
|
||||
r = 10
|
||||
if strip_prefix:
|
||||
if len(string) >= 2 and string[:2] in ('0x', '0X'):
|
||||
string = string[2:]
|
||||
r = 16
|
||||
n = 0
|
||||
num = 0
|
||||
while n < len(string):
|
||||
cand = RADIX_CHARS.get(string[n])
|
||||
if cand is None or not cand < r:
|
||||
break
|
||||
num = cand + num * r
|
||||
n += 1
|
||||
if not n:
|
||||
return NaN
|
||||
return sign * num
|
||||
|
||||
|
||||
@Js
|
||||
def parseFloat(string):
|
||||
string = string.to_string().value.strip()
|
||||
sign = 1
|
||||
if string and string[0] in ('+', '-'):
|
||||
if string[0] == '-':
|
||||
sign = -1
|
||||
string = string[1:]
|
||||
num = None
|
||||
length = 1
|
||||
max_len = None
|
||||
failed = 0
|
||||
while length <= len(string):
|
||||
try:
|
||||
num = float(string[:length])
|
||||
max_len = length
|
||||
failed = 0
|
||||
except:
|
||||
failed += 1
|
||||
if failed > 4: # cant be a number anymore
|
||||
break
|
||||
length += 1
|
||||
if num is None:
|
||||
return NaN
|
||||
return sign * float(string[:max_len])
|
||||
|
||||
|
||||
@Js
|
||||
def isNaN(number):
|
||||
if number.to_number().is_nan():
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
@Js
|
||||
def isFinite(number):
|
||||
num = number.to_number()
|
||||
if num.is_nan() or num.is_infinity():
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
# 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)
|
||||
@@ -1,925 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
import re
|
||||
|
||||
import datetime
|
||||
|
||||
from desc import *
|
||||
from simplex import *
|
||||
from conversions import *
|
||||
import six
|
||||
from pyjsparser import PyJsParser
|
||||
from itertools import izip
|
||||
|
||||
from conversions import *
|
||||
from simplex import *
|
||||
|
||||
|
||||
def Type(obj):
|
||||
return obj.TYPE
|
||||
|
||||
|
||||
# 8.6.2
|
||||
class PyJs(object):
|
||||
TYPE = 'Object'
|
||||
IS_CONSTRUCTOR = False
|
||||
|
||||
prototype = None
|
||||
Class = None
|
||||
extensible = True
|
||||
value = None
|
||||
|
||||
own = {}
|
||||
|
||||
def get_member(self, unconverted_prop):
|
||||
return self.get(to_string(unconverted_prop))
|
||||
|
||||
def put_member(self, unconverted_prop, val):
|
||||
return self.put(to_string(unconverted_prop), val)
|
||||
|
||||
def get(self, prop):
|
||||
assert type(prop) == unicode
|
||||
cand = self.get_property(prop)
|
||||
if cand is None:
|
||||
return undefined
|
||||
if is_data_descriptor(cand):
|
||||
return cand['value']
|
||||
if is_undefined(cand['get']):
|
||||
return undefined
|
||||
return cand['get'].call(self)
|
||||
|
||||
def get_own_property(self, prop):
|
||||
assert type(prop) == unicode
|
||||
# takes py returns py
|
||||
return self.own.get(prop)
|
||||
|
||||
def get_property(self, prop):
|
||||
assert type(prop) == unicode
|
||||
# take py returns py
|
||||
cand = self.get_own_property(prop)
|
||||
if cand:
|
||||
return cand
|
||||
if self.prototype is not None:
|
||||
return self.prototype.get_property(prop)
|
||||
|
||||
def put(self, prop, val, throw=False):
|
||||
assert type(prop) == unicode
|
||||
# takes py, returns none
|
||||
if not self.can_put(prop):
|
||||
if throw:
|
||||
raise MakeError('TypeError', 'Could not define own property')
|
||||
return
|
||||
own_desc = self.get_own_property(prop)
|
||||
if is_data_descriptor(own_desc):
|
||||
self.own[prop]['value'] = val
|
||||
return
|
||||
desc = self.get_property(prop)
|
||||
if is_accessor_descriptor(desc):
|
||||
desc['set'].call(
|
||||
self, (val, )) # calling setter on own or inherited element
|
||||
else: # new property
|
||||
self.own[prop] = {
|
||||
'value': val,
|
||||
'writable': True,
|
||||
'configurable': True,
|
||||
'enumerable': True
|
||||
}
|
||||
|
||||
def can_put(self, prop): # to check
|
||||
assert type(prop) == unicode, type(prop)
|
||||
# takes py returns py
|
||||
desc = self.get_own_property(prop)
|
||||
if desc: # if we have this property
|
||||
if is_accessor_descriptor(desc):
|
||||
return is_callable(
|
||||
desc['set']) # Check if setter method is defined
|
||||
else: # data desc
|
||||
return desc['writable']
|
||||
if self.prototype is None:
|
||||
return self.extensible
|
||||
inherited = self.prototype.get_property(prop)
|
||||
if inherited is None:
|
||||
return self.extensible
|
||||
if is_accessor_descriptor(inherited):
|
||||
return not is_undefined(inherited['set'])
|
||||
elif self.extensible:
|
||||
return inherited['writable'] # weird...
|
||||
return False
|
||||
|
||||
def has_property(self, prop):
|
||||
assert type(prop) == unicode
|
||||
# takes py returns Py
|
||||
return self.get_property(prop) is not None
|
||||
|
||||
def delete(self, prop, throw=False):
|
||||
assert type(prop) == unicode
|
||||
# takes py, returns py
|
||||
desc = self.get_own_property(prop)
|
||||
if desc is None:
|
||||
return True
|
||||
if desc['configurable']:
|
||||
del self.own[prop]
|
||||
return True
|
||||
if throw:
|
||||
raise MakeError('TypeError', 'Could not define own property')
|
||||
return False
|
||||
|
||||
def default_value(self, hint=None):
|
||||
order = ('valueOf', 'toString')
|
||||
if hint == 'String' or (hint is None and self.Class == 'Date'):
|
||||
order = ('toString', 'valueOf')
|
||||
for meth_name in order:
|
||||
method = self.get(meth_name)
|
||||
if method is not None and is_callable(method):
|
||||
cand = method.call(self, ())
|
||||
if is_primitive(cand):
|
||||
return cand
|
||||
raise MakeError('TypeError',
|
||||
'Cannot convert object to primitive value')
|
||||
|
||||
def define_own_property(
|
||||
self, prop, desc,
|
||||
throw): # Internal use only. External through Object
|
||||
assert type(prop) == unicode
|
||||
# takes Py, returns Py
|
||||
# prop must be a Py string. Desc is either a descriptor or accessor.
|
||||
# Messy method - raw translation from Ecma spec to prevent any bugs. # todo check this
|
||||
current = self.get_own_property(prop)
|
||||
|
||||
extensible = self.extensible
|
||||
if not current: # We are creating a new OWN property
|
||||
if not extensible:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
# extensible must be True
|
||||
if is_data_descriptor(desc) or is_generic_descriptor(desc):
|
||||
DEFAULT_DATA_DESC = {
|
||||
'value': undefined, # undefined
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
DEFAULT_DATA_DESC.update(desc)
|
||||
self.own[prop] = DEFAULT_DATA_DESC
|
||||
else:
|
||||
DEFAULT_ACCESSOR_DESC = {
|
||||
'get': undefined, # undefined
|
||||
'set': undefined, # undefined
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
DEFAULT_ACCESSOR_DESC.update(desc)
|
||||
self.own[prop] = DEFAULT_ACCESSOR_DESC
|
||||
return True
|
||||
# therefore current exists!
|
||||
if not desc or desc == current: # We don't need to change anything.
|
||||
return True
|
||||
configurable = current['configurable']
|
||||
if not configurable: # Prevent changing params
|
||||
if desc.get('configurable'):
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if 'enumerable' in desc and desc['enumerable'] != current[
|
||||
'enumerable']:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if is_generic_descriptor(desc):
|
||||
pass
|
||||
elif is_data_descriptor(current) != is_data_descriptor(desc):
|
||||
# we want to change the current type of property
|
||||
if not configurable:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if is_data_descriptor(current): # from data to setter
|
||||
del current['value']
|
||||
del current['writable']
|
||||
current['set'] = undefined # undefined
|
||||
current['get'] = undefined # undefined
|
||||
else: # from setter to data
|
||||
del current['set']
|
||||
del current['get']
|
||||
current['value'] = undefined # undefined
|
||||
current['writable'] = False
|
||||
elif is_data_descriptor(current) and is_data_descriptor(desc):
|
||||
if not configurable:
|
||||
if not current['writable'] and desc.get('writable'):
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if not current['writable'] and 'value' in desc and current[
|
||||
'value'] != desc['value']:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
elif is_accessor_descriptor(current) and is_accessor_descriptor(desc):
|
||||
if not configurable:
|
||||
if 'set' in desc and desc['set'] != current['set']:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if 'get' in desc and desc['get'] != current['get']:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
current.update(desc)
|
||||
return True
|
||||
|
||||
def create(self, args, space):
|
||||
'''Generally not a constructor, raise an error'''
|
||||
raise MakeError('TypeError', '%s is not a constructor' % self.Class)
|
||||
|
||||
|
||||
def get_member(
|
||||
self, prop, space
|
||||
): # general member getter, prop has to be unconverted prop. it is it can be any value
|
||||
typ = type(self)
|
||||
if typ not in PRIMITIVES: # most likely getter for object
|
||||
return self.get_member(
|
||||
prop
|
||||
) # <- object can implement this to support faster prop getting. ex array.
|
||||
elif typ == unicode: # then probably a String
|
||||
if type(prop) == float and is_finite(prop):
|
||||
index = int(prop)
|
||||
if index == prop and 0 <= index < len(self):
|
||||
return self[index]
|
||||
s_prop = to_string(prop)
|
||||
if s_prop == 'length':
|
||||
return float(len(self))
|
||||
elif s_prop.isdigit():
|
||||
index = int(s_prop)
|
||||
if 0 <= index < len(self):
|
||||
return self[index]
|
||||
# use standard string prototype
|
||||
return space.StringPrototype.get(s_prop)
|
||||
# maybe an index
|
||||
elif typ == float:
|
||||
# use standard number prototype
|
||||
return space.NumberPrototype.get(to_string(prop))
|
||||
elif typ == bool:
|
||||
return space.BooleanPrototype.get(to_string(prop))
|
||||
elif typ is UNDEFINED_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot read property '%s' of undefined" % prop)
|
||||
elif typ is NULL_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot read property '%s' of null" % prop)
|
||||
else:
|
||||
raise RuntimeError('Unknown type! - ' + repr(typ))
|
||||
|
||||
|
||||
def get_member_dot(self, prop, space):
|
||||
# dot member getter, prop has to be unicode
|
||||
typ = type(self)
|
||||
if typ not in PRIMITIVES: # most likely getter for object
|
||||
return self.get(prop)
|
||||
elif typ == unicode: # then probably a String
|
||||
if prop == 'length':
|
||||
return float(len(self))
|
||||
elif prop.isdigit():
|
||||
index = int(prop)
|
||||
if 0 <= index < len(self):
|
||||
return self[index]
|
||||
else:
|
||||
# use standard string prototype
|
||||
return space.StringPrototype.get(prop)
|
||||
# maybe an index
|
||||
elif typ == float:
|
||||
# use standard number prototype
|
||||
return space.NumberPrototype.get(prop)
|
||||
elif typ == bool:
|
||||
return space.BooleanPrototype.get(prop)
|
||||
elif typ in (UNDEFINED_TYPE, NULL_TYPE):
|
||||
raise MakeError('TypeError',
|
||||
"Cannot read property '%s' of undefined" % prop)
|
||||
else:
|
||||
raise RuntimeError('Unknown type! - ' + repr(typ))
|
||||
|
||||
|
||||
# Object
|
||||
|
||||
|
||||
class PyJsObject(PyJs):
|
||||
TYPE = 'Object'
|
||||
Class = 'Object'
|
||||
|
||||
def __init__(self, prototype=None):
|
||||
self.prototype = prototype
|
||||
self.own = {}
|
||||
|
||||
def _init(self, props, vals):
|
||||
i = 0
|
||||
for prop, kind in props:
|
||||
if prop in self.own: # just check... probably will not happen very often.
|
||||
if is_data_descriptor(self.own[prop]):
|
||||
if kind != 'i':
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid object initializer! Duplicate property name "%s"'
|
||||
% prop)
|
||||
else:
|
||||
if kind == 'i' or (kind == 'g' and 'get' in self.own[prop]
|
||||
) or (kind == 's'
|
||||
and 'set' in self.own[prop]):
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid object initializer! Duplicate setter/getter of prop: "%s"'
|
||||
% prop)
|
||||
|
||||
if kind == 'i': # init
|
||||
self.own[prop] = {
|
||||
'value': vals[i],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}
|
||||
elif kind == 'g': # get
|
||||
self.define_own_property(prop, {
|
||||
'get': vals[i],
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
elif kind == 's': # get
|
||||
self.define_own_property(prop, {
|
||||
'get': vals[i],
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
'Invalid property kind - %s. Expected one of i, g, s.' %
|
||||
repr(kind))
|
||||
i += 1
|
||||
|
||||
def _set_props(self, prop_descs):
|
||||
for prop, desc in six.iteritems(prop_descs):
|
||||
self.define_own_property(prop, desc)
|
||||
|
||||
|
||||
# Array
|
||||
|
||||
|
||||
# todo Optimise Array - extremely slow due to index conversions from str to int and back etc.
|
||||
# solution - make get and put methods callable with any type of prop and handle conversions from inside
|
||||
# if not array then use to_string(prop). In array if prop is integer then just use it
|
||||
# also consider removal of these stupid writable, enumerable etc for ints.
|
||||
class PyJsArray(PyJs):
|
||||
Class = 'Array'
|
||||
|
||||
def __init__(self, length, prototype=None):
|
||||
self.prototype = prototype
|
||||
self.own = {
|
||||
'length': {
|
||||
'value': float(length),
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
}
|
||||
|
||||
def _init(self, elements):
|
||||
for i, ele in enumerate(elements):
|
||||
if ele is None: continue
|
||||
self.own[unicode(i)] = {
|
||||
'value': ele,
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}
|
||||
|
||||
def put(self, prop, val, throw=False):
|
||||
assert type(val) != int
|
||||
# takes py, returns none
|
||||
if not self.can_put(prop):
|
||||
if throw:
|
||||
raise MakeError('TypeError', 'Could not define own property')
|
||||
return
|
||||
own_desc = self.get_own_property(prop)
|
||||
if is_data_descriptor(own_desc):
|
||||
self.define_own_property(prop, {'value': val}, False)
|
||||
return
|
||||
desc = self.get_property(prop)
|
||||
if is_accessor_descriptor(desc):
|
||||
desc['set'].call(
|
||||
self, (val, )) # calling setter on own or inherited element
|
||||
else: # new property
|
||||
self.define_own_property(
|
||||
prop, {
|
||||
'value': val,
|
||||
'writable': True,
|
||||
'configurable': True,
|
||||
'enumerable': True
|
||||
}, False)
|
||||
|
||||
def define_own_property(self, prop, desc, throw):
|
||||
assert type(desc.get('value')) != int
|
||||
old_len_desc = self.get_own_property('length')
|
||||
old_len = old_len_desc['value'] # value is js type so convert to py.
|
||||
if prop == 'length':
|
||||
if 'value' not in desc:
|
||||
return PyJs.define_own_property(self, prop, desc, False)
|
||||
new_len = to_uint32(desc['value'])
|
||||
if new_len != to_number(desc['value']):
|
||||
raise MakeError('RangeError', 'Invalid range!')
|
||||
new_desc = dict((k, v) for k, v in six.iteritems(desc))
|
||||
new_desc['value'] = float(new_len)
|
||||
if new_len >= old_len:
|
||||
return PyJs.define_own_property(self, prop, new_desc, False)
|
||||
if not old_len_desc['writable']:
|
||||
return False
|
||||
if 'writable' not in new_desc or new_desc['writable'] == True:
|
||||
new_writable = True
|
||||
else:
|
||||
new_writable = False
|
||||
new_desc['writable'] = True
|
||||
if not PyJs.define_own_property(self, prop, new_desc, False):
|
||||
return False
|
||||
if new_len < old_len:
|
||||
# not very efficient for sparse arrays, so using different method for sparse:
|
||||
if old_len > 30 * len(self.own):
|
||||
for ele in self.own.keys():
|
||||
if ele.isdigit() and int(ele) >= new_len:
|
||||
if not self.delete(
|
||||
ele
|
||||
): # if failed to delete set len to current len and reject.
|
||||
new_desc['value'] = old_len + 1.
|
||||
if not new_writable:
|
||||
new_desc['writable'] = False
|
||||
PyJs.define_own_property(
|
||||
self, prop, new_desc, False)
|
||||
return False
|
||||
old_len = new_len
|
||||
else: # standard method
|
||||
while new_len < old_len:
|
||||
old_len -= 1
|
||||
if not self.delete(
|
||||
unicode(int(old_len))
|
||||
): # if failed to delete set len to current len and reject.
|
||||
new_desc['value'] = old_len + 1.
|
||||
if not new_writable:
|
||||
new_desc['writable'] = False
|
||||
PyJs.define_own_property(self, prop, new_desc,
|
||||
False)
|
||||
return False
|
||||
if not new_writable:
|
||||
self.own['length']['writable'] = False
|
||||
return True
|
||||
|
||||
elif prop.isdigit():
|
||||
index = to_uint32(prop)
|
||||
if index >= old_len and not old_len_desc['writable']:
|
||||
return False
|
||||
if not PyJs.define_own_property(self, prop, desc, False):
|
||||
return False
|
||||
if index >= old_len:
|
||||
old_len_desc['value'] = index + 1.
|
||||
return True
|
||||
else:
|
||||
return PyJs.define_own_property(self, prop, desc, False)
|
||||
|
||||
def to_list(self):
|
||||
return [
|
||||
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
|
||||
]
|
||||
|
||||
|
||||
# database with compiled patterns. Js pattern -> Py pattern.
|
||||
REGEXP_DB = {}
|
||||
|
||||
|
||||
class PyJsRegExp(PyJs):
|
||||
Class = 'RegExp'
|
||||
|
||||
def __init__(self, body, flags, prototype=None):
|
||||
self.prototype = prototype
|
||||
self.glob = True if 'g' in flags else False
|
||||
self.ignore_case = re.IGNORECASE if 'i' in flags else 0
|
||||
self.multiline = re.MULTILINE if 'm' in flags else 0
|
||||
self.value = body
|
||||
|
||||
if (body, flags) in REGEXP_DB:
|
||||
self.pat = REGEXP_DB[body, flags]
|
||||
else:
|
||||
comp = None
|
||||
try:
|
||||
# converting JS regexp pattern to Py pattern.
|
||||
possible_fixes = [(u'[]', u'[\0]'), (u'[^]', u'[^\0]'),
|
||||
(u'nofix1791', u'nofix1791')]
|
||||
reg = self.value
|
||||
for fix, rep in possible_fixes:
|
||||
comp = PyJsParser()._interpret_regexp(reg, flags)
|
||||
#print 'reg -> comp', reg, '->', comp
|
||||
try:
|
||||
self.pat = re.compile(
|
||||
comp, self.ignore_case | self.multiline)
|
||||
#print reg, '->', comp
|
||||
break
|
||||
except:
|
||||
reg = reg.replace(fix, rep)
|
||||
# print 'Fix', fix, '->', rep, '=', reg
|
||||
else:
|
||||
raise Exception()
|
||||
REGEXP_DB[body, flags] = self.pat
|
||||
except:
|
||||
#print 'Invalid pattern...', self.value, comp
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid RegExp pattern: %s -> %s' % (repr(self.value),
|
||||
repr(comp)))
|
||||
# now set own properties:
|
||||
self.own = {
|
||||
'source': {
|
||||
'value': self.value,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
},
|
||||
'global': {
|
||||
'value': self.glob,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
},
|
||||
'ignoreCase': {
|
||||
'value': bool(self.ignore_case),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
},
|
||||
'multiline': {
|
||||
'value': bool(self.multiline),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
},
|
||||
'lastIndex': {
|
||||
'value': 0.,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': False
|
||||
}
|
||||
}
|
||||
|
||||
def match(self, string, pos):
|
||||
'''string is of course a py string'''
|
||||
return self.pat.match(string, int(pos))
|
||||
|
||||
|
||||
class PyJsError(PyJs):
|
||||
Class = 'Error'
|
||||
extensible = True
|
||||
|
||||
def __init__(self, message=None, prototype=None):
|
||||
self.prototype = prototype
|
||||
self.own = {}
|
||||
if message is not None:
|
||||
self.put('message', to_string(message))
|
||||
self.own['message']['enumerable'] = False
|
||||
|
||||
|
||||
class PyJsDate(PyJs):
|
||||
Class = 'Date'
|
||||
UTCToLocal = None # todo UTC to local should be imported!
|
||||
|
||||
def __init__(self, value, prototype=None):
|
||||
self.value = value
|
||||
self.own = {}
|
||||
self.prototype = prototype
|
||||
|
||||
# todo fix this problematic datetime part
|
||||
def to_local_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(
|
||||
self.UTCToLocal(self.value) // 1000)
|
||||
|
||||
def to_utc_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(self.value // 1000)
|
||||
|
||||
def local_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_local_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
def utc_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_utc_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
|
||||
# Scope class it will hold all the variables accessible to user
|
||||
class Scope(PyJs):
|
||||
Class = 'Global'
|
||||
extensible = True
|
||||
IS_CHILD_SCOPE = True
|
||||
THIS_BINDING = None
|
||||
space = None
|
||||
exe = None
|
||||
|
||||
# todo speed up!
|
||||
# in order to speed up this very important class the top scope should behave differently than
|
||||
# child scopes, child scope should not have this property descriptor thing because they cant be changed anyway
|
||||
# they are all confugurable= False
|
||||
|
||||
def __init__(self, scope, space, parent=None):
|
||||
"""Doc"""
|
||||
self.space = space
|
||||
self.prototype = parent
|
||||
if type(scope) is not dict:
|
||||
assert parent is not None, 'You initialised the WITH_SCOPE without a parent scope.'
|
||||
self.own = scope
|
||||
self.is_with_scope = True
|
||||
else:
|
||||
self.is_with_scope = False
|
||||
if parent is None:
|
||||
# global, top level scope
|
||||
self.own = {}
|
||||
for k, v in six.iteritems(scope):
|
||||
# set all the global items
|
||||
self.define_own_property(
|
||||
k, {
|
||||
'value': v,
|
||||
'configurable': False,
|
||||
'writable': False,
|
||||
'enumerable': False
|
||||
}, False)
|
||||
else:
|
||||
# not global, less powerful but faster closure.
|
||||
self.own = scope # simple dictionary which maps name directly to js object.
|
||||
|
||||
self.par = super(Scope, self)
|
||||
self.stack = []
|
||||
|
||||
def register(self, var):
|
||||
# registered keeps only global registered variables
|
||||
if self.prototype is None:
|
||||
# define in global scope
|
||||
if var in self.own:
|
||||
self.own[var]['configurable'] = False
|
||||
else:
|
||||
self.define_own_property(
|
||||
var, {
|
||||
'value': undefined,
|
||||
'configurable': False,
|
||||
'writable': True,
|
||||
'enumerable': True
|
||||
}, False)
|
||||
elif var not in self.own:
|
||||
# define in local scope since it has not been defined yet
|
||||
self.own[var] = undefined # default value
|
||||
|
||||
def registers(self, vars):
|
||||
"""register multiple variables"""
|
||||
for var in vars:
|
||||
self.register(var)
|
||||
|
||||
def put(self, var, val, throw=False):
|
||||
if self.prototype is None:
|
||||
desc = self.own.get(var) # global scope
|
||||
if desc is None:
|
||||
self.par.put(var, val, False)
|
||||
else:
|
||||
if desc['writable']: # todo consider getters/setters
|
||||
desc['value'] = val
|
||||
else:
|
||||
if self.is_with_scope:
|
||||
if self.own.has_property(var):
|
||||
return self.own.put(var, val, throw=throw)
|
||||
else:
|
||||
return self.prototype.put(var, val)
|
||||
# trying to put in local scope
|
||||
# we dont know yet in which scope we should place this var
|
||||
elif var in self.own:
|
||||
self.own[var] = val
|
||||
return val
|
||||
else:
|
||||
# try to put in the lower scope since we cant put in this one (var wasn't registered)
|
||||
return self.prototype.put(var, val)
|
||||
|
||||
def get(self, var, throw=False):
|
||||
if self.prototype is not None:
|
||||
if self.is_with_scope:
|
||||
cand = None if not self.own.has_property(
|
||||
var) else self.own.get(var)
|
||||
else:
|
||||
# fast local scope
|
||||
cand = self.own.get(var)
|
||||
if cand is None:
|
||||
return self.prototype.get(var, throw)
|
||||
return cand
|
||||
# slow, global scope
|
||||
if var not in self.own:
|
||||
# try in ObjectPrototype...
|
||||
if var in self.space.ObjectPrototype.own:
|
||||
return self.space.ObjectPrototype.get(var)
|
||||
if throw:
|
||||
raise MakeError('ReferenceError', '%s is not defined' % var)
|
||||
return undefined
|
||||
cand = self.own[var].get('value')
|
||||
return cand if cand is not None else self.own[var]['get'].call(self)
|
||||
|
||||
def delete(self, var, throw=False):
|
||||
if self.prototype is not None:
|
||||
if self.is_with_scope:
|
||||
if self.own.has_property(var):
|
||||
return self.own.delete(var)
|
||||
elif var in self.own:
|
||||
return False
|
||||
return self.prototype.delete(var)
|
||||
# we are in global scope here. Must exist and be configurable to delete
|
||||
if var not in self.own:
|
||||
# this var does not exist, why do you want to delete it???
|
||||
return True
|
||||
if self.own[var]['configurable']:
|
||||
del self.own[var]
|
||||
return True
|
||||
# not configurable, cant delete
|
||||
return False
|
||||
|
||||
|
||||
def get_new_arguments_obj(args, space):
|
||||
obj = space.NewObject()
|
||||
obj.Class = 'Arguments'
|
||||
obj.define_own_property(
|
||||
'length', {
|
||||
'value': float(len(args)),
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': True
|
||||
}, False)
|
||||
for i, e in enumerate(args):
|
||||
obj.put(unicode(i), e)
|
||||
return obj
|
||||
|
||||
|
||||
#Function
|
||||
class PyJsFunction(PyJs):
|
||||
Class = 'Function'
|
||||
source = '{ [native code] }'
|
||||
IS_CONSTRUCTOR = True
|
||||
|
||||
def __init__(self,
|
||||
code,
|
||||
ctx,
|
||||
params,
|
||||
name,
|
||||
space,
|
||||
is_declaration,
|
||||
definitions,
|
||||
prototype=None):
|
||||
self.prototype = prototype
|
||||
self.own = {}
|
||||
|
||||
self.code = code
|
||||
if type(
|
||||
self.code
|
||||
) == int: # just a label pointing to the beginning of the code.
|
||||
self.is_native = False
|
||||
else:
|
||||
self.is_native = True # python function
|
||||
|
||||
self.ctx = ctx
|
||||
|
||||
self.params = params
|
||||
self.arguments_in_params = 'arguments' in params
|
||||
self.definitions = definitions
|
||||
|
||||
# todo remove this check later
|
||||
for p in params:
|
||||
assert p in self.definitions
|
||||
|
||||
self.name = name
|
||||
self.space = space
|
||||
self.is_declaration = is_declaration
|
||||
|
||||
#set own property length to the number of arguments
|
||||
self.own['length'] = {
|
||||
'value': float(len(params)),
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
|
||||
if name:
|
||||
self.own['name'] = {
|
||||
'value': name,
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': True
|
||||
}
|
||||
|
||||
if not self.is_native: # set prototype for user defined functions
|
||||
# constructor points to this function
|
||||
proto = space.NewObject()
|
||||
proto.own['constructor'] = {
|
||||
'value': self,
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': True
|
||||
}
|
||||
self.own['prototype'] = {
|
||||
'value': proto,
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
# todo set up throwers on callee and arguments if in strict mode
|
||||
|
||||
def call(self, this, args=()):
|
||||
''' Dont use this method from inside bytecode to call other bytecode. '''
|
||||
if self.is_native:
|
||||
_args = SpaceTuple(
|
||||
args
|
||||
) # we have to do that unfortunately to pass all the necessary info to the funcs
|
||||
_args.space = self.space
|
||||
return self.code(
|
||||
this, _args
|
||||
) # must return valid js object - undefined, null, float, unicode, bool, or PyJs
|
||||
else:
|
||||
return self.space.exe._call(self, this,
|
||||
args) # will run inside bytecode
|
||||
|
||||
def has_instance(self, other):
|
||||
# I am not sure here so instanceof may not work lol.
|
||||
if not is_object(other):
|
||||
return False
|
||||
proto = self.get('prototype')
|
||||
if not is_object(proto):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Function has non-object prototype in instanceof check')
|
||||
while True:
|
||||
other = other.prototype
|
||||
if not other: # todo make sure that the condition is not None or null
|
||||
return False
|
||||
if other is proto:
|
||||
return True
|
||||
|
||||
def create(self, args, space):
|
||||
proto = self.get('prototype')
|
||||
if not is_object(proto):
|
||||
proto = space.ObjectPrototype
|
||||
new = PyJsObject(prototype=proto)
|
||||
res = self.call(new, args)
|
||||
if is_object(res):
|
||||
return res
|
||||
return new
|
||||
|
||||
def _generate_my_context(self, this, args):
|
||||
my_ctx = Scope(
|
||||
dict(izip(self.params, args)), self.space, parent=self.ctx)
|
||||
my_ctx.registers(self.definitions)
|
||||
my_ctx.THIS_BINDING = this
|
||||
if not self.arguments_in_params:
|
||||
my_ctx.own['arguments'] = get_new_arguments_obj(args, self.space)
|
||||
if not self.is_declaration and self.name and self.name not in my_ctx.own:
|
||||
my_ctx.own[
|
||||
self.
|
||||
name] = self # this should be immutable binding but come on!
|
||||
return my_ctx
|
||||
|
||||
|
||||
class SpaceTuple:
|
||||
def __init__(self, tup):
|
||||
self.tup = tup
|
||||
|
||||
def __len__(self):
|
||||
return len(self.tup)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.tup[item]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.tup)
|
||||
@@ -1,752 +0,0 @@
|
||||
from code import Code
|
||||
from simplex import MakeError
|
||||
from opcodes import *
|
||||
from operations import *
|
||||
from trans_utils import *
|
||||
|
||||
SPECIAL_IDENTIFIERS = {'true', 'false', 'this'}
|
||||
|
||||
|
||||
class ByteCodeGenerator:
|
||||
def __init__(self, exe):
|
||||
self.exe = exe
|
||||
|
||||
self.declared_continue_labels = {}
|
||||
self.declared_break_labels = {}
|
||||
|
||||
self.implicit_breaks = []
|
||||
self.implicit_continues = []
|
||||
|
||||
self.declared_vars = []
|
||||
|
||||
self.function_declaration_tape = []
|
||||
|
||||
self.states = []
|
||||
|
||||
def record_state(self):
|
||||
self.states.append(
|
||||
(self.declared_continue_labels, self.declared_break_labels,
|
||||
self.implicit_breaks, self.implicit_continues, self.declared_vars,
|
||||
self.function_declaration_tape))
|
||||
self.declared_continue_labels, self.declared_break_labels, \
|
||||
self.implicit_breaks, self.implicit_continues, \
|
||||
self.declared_vars, self.function_declaration_tape = {}, {}, [], [], [], []
|
||||
|
||||
def restore_state(self):
|
||||
self.declared_continue_labels, self.declared_break_labels, \
|
||||
self.implicit_breaks, self.implicit_continues, \
|
||||
self.declared_vars, self.function_declaration_tape = self.states.pop()
|
||||
|
||||
def ArrayExpression(self, elements, **kwargs):
|
||||
for e in elements:
|
||||
if e is None:
|
||||
self.emit('LOAD_NONE')
|
||||
else:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_ARRAY', len(elements))
|
||||
|
||||
def AssignmentExpression(self, operator, left, right, **kwargs):
|
||||
operator = operator[:-1]
|
||||
if left['type'] == 'MemberExpression':
|
||||
self.emit(left['object'])
|
||||
if left['computed']:
|
||||
self.emit(left['property'])
|
||||
self.emit(right)
|
||||
if operator:
|
||||
self.emit('STORE_MEMBER_OP', operator)
|
||||
else:
|
||||
self.emit('STORE_MEMBER')
|
||||
else:
|
||||
self.emit(right)
|
||||
if operator:
|
||||
self.emit('STORE_MEMBER_DOT_OP', left['property']['name'],
|
||||
operator)
|
||||
else:
|
||||
self.emit('STORE_MEMBER_DOT', left['property']['name'])
|
||||
elif left['type'] == 'Identifier':
|
||||
if left['name'] in SPECIAL_IDENTIFIERS:
|
||||
raise MakeError('SyntaxError',
|
||||
'Invalid left-hand side in assignment')
|
||||
self.emit(right)
|
||||
if operator:
|
||||
self.emit('STORE_OP', left['name'], operator)
|
||||
else:
|
||||
self.emit('STORE', left['name'])
|
||||
else:
|
||||
raise MakeError('SyntaxError',
|
||||
'Invalid left-hand side in assignment')
|
||||
|
||||
def BinaryExpression(self, operator, left, right, **kwargs):
|
||||
self.emit(left)
|
||||
self.emit(right)
|
||||
self.emit('BINARY_OP', operator)
|
||||
|
||||
def BlockStatement(self, body, **kwargs):
|
||||
self._emit_statement_list(body)
|
||||
|
||||
def BreakStatement(self, label, **kwargs):
|
||||
if label is None:
|
||||
self.emit('JUMP', self.implicit_breaks[-1])
|
||||
else:
|
||||
label = label.get('name')
|
||||
if label not in self.declared_break_labels:
|
||||
raise MakeError('SyntaxError',
|
||||
'Undefined label \'%s\'' % label)
|
||||
else:
|
||||
self.emit('JUMP', self.declared_break_labels[label])
|
||||
|
||||
def CallExpression(self, callee, arguments, **kwargs):
|
||||
if callee['type'] == 'MemberExpression':
|
||||
self.emit(callee['object'])
|
||||
if callee['computed']:
|
||||
self.emit(callee['property'])
|
||||
if arguments:
|
||||
for e in arguments:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_N_TUPLE', len(arguments))
|
||||
self.emit('CALL_METHOD')
|
||||
else:
|
||||
self.emit('CALL_METHOD_NO_ARGS')
|
||||
else:
|
||||
prop_name = to_key(callee['property'])
|
||||
if arguments:
|
||||
for e in arguments:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_N_TUPLE', len(arguments))
|
||||
self.emit('CALL_METHOD_DOT', prop_name)
|
||||
else:
|
||||
self.emit('CALL_METHOD_DOT_NO_ARGS', prop_name)
|
||||
else:
|
||||
self.emit(callee)
|
||||
if arguments:
|
||||
for e in arguments:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_N_TUPLE', len(arguments))
|
||||
self.emit('CALL')
|
||||
else:
|
||||
self.emit('CALL_NO_ARGS')
|
||||
|
||||
def ClassBody(self, body, **kwargs):
|
||||
raise NotImplementedError('Not available in ECMA 5.1')
|
||||
|
||||
def ClassDeclaration(self, id, superClass, body, **kwargs):
|
||||
raise NotImplementedError('Not available in ECMA 5.1')
|
||||
|
||||
def ClassExpression(self, id, superClass, body, **kwargs):
|
||||
raise NotImplementedError('Classes not available in ECMA 5.1')
|
||||
|
||||
def ConditionalExpression(self, test, consequent, alternate, **kwargs):
|
||||
alt = self.exe.get_new_label()
|
||||
end = self.exe.get_new_label()
|
||||
# ?
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', alt)
|
||||
# first val
|
||||
self.emit(consequent)
|
||||
self.emit('JUMP', end)
|
||||
# second val
|
||||
self.emit('LABEL', alt)
|
||||
self.emit(alternate)
|
||||
# end of ?: statement
|
||||
self.emit('LABEL', end)
|
||||
|
||||
def ContinueStatement(self, label, **kwargs):
|
||||
if label is None:
|
||||
self.emit('JUMP', self.implicit_continues[-1])
|
||||
else:
|
||||
label = label.get('name')
|
||||
if label not in self.declared_continue_labels:
|
||||
raise MakeError('SyntaxError',
|
||||
'Undefined label \'%s\'' % label)
|
||||
else:
|
||||
self.emit('JUMP', self.declared_continue_labels[label])
|
||||
|
||||
def DebuggerStatement(self, **kwargs):
|
||||
self.EmptyStatement(**kwargs)
|
||||
|
||||
def DoWhileStatement(self, body, test, **kwargs):
|
||||
continue_label = self.exe.get_new_label()
|
||||
break_label = self.exe.get_new_label()
|
||||
initial_do = self.exe.get_new_label()
|
||||
|
||||
self.emit('JUMP', initial_do)
|
||||
self.emit('LABEL', continue_label)
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', break_label)
|
||||
self.emit('LABEL', initial_do)
|
||||
|
||||
# translate the body, remember to add and afterwards remove implicit break/continue labels
|
||||
|
||||
self.implicit_continues.append(continue_label)
|
||||
self.implicit_breaks.append(break_label)
|
||||
self.emit(body)
|
||||
self.implicit_continues.pop()
|
||||
self.implicit_breaks.pop()
|
||||
|
||||
self.emit('JUMP', continue_label) # loop back
|
||||
self.emit('LABEL', break_label)
|
||||
|
||||
def EmptyStatement(self, **kwargs):
|
||||
# do nothing
|
||||
pass
|
||||
|
||||
def ExpressionStatement(self, expression, **kwargs):
|
||||
# change the final stack value
|
||||
# pop the previous value and execute expression
|
||||
self.emit('POP')
|
||||
self.emit(expression)
|
||||
|
||||
def ForStatement(self, init, test, update, body, **kwargs):
|
||||
continue_label = self.exe.get_new_label()
|
||||
break_label = self.exe.get_new_label()
|
||||
first_start = self.exe.get_new_label()
|
||||
|
||||
if init is not None:
|
||||
self.emit(init)
|
||||
if init['type'] != 'VariableDeclaration':
|
||||
self.emit('POP')
|
||||
|
||||
# skip first update and go straight to test
|
||||
self.emit('JUMP', first_start)
|
||||
|
||||
self.emit('LABEL', continue_label)
|
||||
if update:
|
||||
self.emit(update)
|
||||
self.emit('POP')
|
||||
self.emit('LABEL', first_start)
|
||||
if test:
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', break_label)
|
||||
|
||||
# translate the body, remember to add and afterwards to remove implicit break/continue labels
|
||||
|
||||
self.implicit_continues.append(continue_label)
|
||||
self.implicit_breaks.append(break_label)
|
||||
self.emit(body)
|
||||
self.implicit_continues.pop()
|
||||
self.implicit_breaks.pop()
|
||||
|
||||
self.emit('JUMP', continue_label) # loop back
|
||||
self.emit('LABEL', break_label)
|
||||
|
||||
def ForInStatement(self, left, right, body, **kwargs):
|
||||
# prepare the needed labels
|
||||
body_start_label = self.exe.get_new_label()
|
||||
continue_label = self.exe.get_new_label()
|
||||
break_label = self.exe.get_new_label()
|
||||
|
||||
# prepare the name
|
||||
if left['type'] == 'VariableDeclaration':
|
||||
if len(left['declarations']) != 1:
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
' Invalid left-hand side in for-in loop: Must have a single binding.'
|
||||
)
|
||||
self.emit(left)
|
||||
name = left['declarations'][0]['id']['name']
|
||||
elif left['type'] == 'Identifier':
|
||||
name = left['name']
|
||||
else:
|
||||
raise MakeError('SyntaxError',
|
||||
'Invalid left-hand side in for-loop')
|
||||
|
||||
# prepare the iterable
|
||||
self.emit(right)
|
||||
|
||||
# emit ForIn Opcode
|
||||
self.emit('FOR_IN', name, body_start_label, continue_label,
|
||||
break_label)
|
||||
|
||||
# a special continue position
|
||||
self.emit('LABEL', continue_label)
|
||||
self.emit('NOP')
|
||||
|
||||
self.emit('LABEL', body_start_label)
|
||||
self.implicit_continues.append(continue_label)
|
||||
self.implicit_breaks.append(break_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
self.emit(body)
|
||||
self.implicit_continues.pop()
|
||||
self.implicit_breaks.pop()
|
||||
self.emit('NOP')
|
||||
self.emit('LABEL', break_label)
|
||||
self.emit('NOP')
|
||||
|
||||
def FunctionDeclaration(self, id, params, defaults, body, **kwargs):
|
||||
if defaults:
|
||||
raise NotImplementedError('Defaults not available in ECMA 5.1')
|
||||
|
||||
# compile function
|
||||
self.record_state(
|
||||
) # cleans translator state and appends it to the stack so that it can be later restored
|
||||
function_start = self.exe.get_new_label()
|
||||
function_declarations = self.exe.get_new_label()
|
||||
declarations_done = self.exe.get_new_label(
|
||||
) # put jump to this place at the and of function tape!
|
||||
function_end = self.exe.get_new_label()
|
||||
|
||||
# skip the function if encountered externally
|
||||
self.emit('JUMP', function_end)
|
||||
|
||||
self.emit('LABEL', function_start)
|
||||
# call is made with empty stack so load undefined to fill it
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
# declare all functions
|
||||
self.emit('JUMP', function_declarations)
|
||||
self.emit('LABEL', declarations_done)
|
||||
self.function_declaration_tape.append(LABEL(function_declarations))
|
||||
|
||||
self.emit(body)
|
||||
self.ReturnStatement(None)
|
||||
|
||||
self.function_declaration_tape.append(JUMP(declarations_done))
|
||||
self.exe.tape.extend(self.function_declaration_tape)
|
||||
|
||||
self.emit('LABEL', function_end)
|
||||
declared_vars = self.declared_vars
|
||||
self.restore_state()
|
||||
|
||||
# create function object and append to stack
|
||||
name = id.get('name')
|
||||
assert name is not None
|
||||
self.declared_vars.append(name)
|
||||
self.function_declaration_tape.append(
|
||||
LOAD_FUNCTION(function_start, tuple(p['name'] for p in params),
|
||||
name, True, tuple(declared_vars)))
|
||||
self.function_declaration_tape.append(STORE(name))
|
||||
self.function_declaration_tape.append(POP())
|
||||
|
||||
def FunctionExpression(self, id, params, defaults, body, **kwargs):
|
||||
if defaults:
|
||||
raise NotImplementedError('Defaults not available in ECMA 5.1')
|
||||
|
||||
# compile function
|
||||
self.record_state(
|
||||
) # cleans translator state and appends it to the stack so that it can be later restored
|
||||
function_start = self.exe.get_new_label()
|
||||
function_declarations = self.exe.get_new_label()
|
||||
declarations_done = self.exe.get_new_label(
|
||||
) # put jump to this place at the and of function tape!
|
||||
function_end = self.exe.get_new_label()
|
||||
|
||||
# skip the function if encountered externally
|
||||
self.emit('JUMP', function_end)
|
||||
|
||||
self.emit('LABEL', function_start)
|
||||
# call is made with empty stack so load undefined to fill it
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
# declare all functions
|
||||
self.emit('JUMP', function_declarations)
|
||||
self.emit('LABEL', declarations_done)
|
||||
self.function_declaration_tape.append(LABEL(function_declarations))
|
||||
|
||||
self.emit(body)
|
||||
self.ReturnStatement(None)
|
||||
|
||||
self.function_declaration_tape.append(JUMP(declarations_done))
|
||||
self.exe.tape.extend(self.function_declaration_tape)
|
||||
|
||||
self.emit('LABEL', function_end)
|
||||
declared_vars = self.declared_vars
|
||||
self.restore_state()
|
||||
|
||||
# create function object and append to stack
|
||||
name = id.get('name') if id else None
|
||||
self.emit('LOAD_FUNCTION', function_start,
|
||||
tuple(p['name'] for p in params), name, False,
|
||||
tuple(declared_vars))
|
||||
|
||||
def Identifier(self, name, **kwargs):
|
||||
if name == 'true':
|
||||
self.emit('LOAD_BOOLEAN', 1)
|
||||
elif name == 'false':
|
||||
self.emit('LOAD_BOOLEAN', 0)
|
||||
elif name == 'undefined':
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
else:
|
||||
self.emit('LOAD', name)
|
||||
|
||||
def IfStatement(self, test, consequent, alternate, **kwargs):
|
||||
alt = self.exe.get_new_label()
|
||||
end = self.exe.get_new_label()
|
||||
# if
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', alt)
|
||||
# consequent
|
||||
self.emit(consequent)
|
||||
self.emit('JUMP', end)
|
||||
# alternate
|
||||
self.emit('LABEL', alt)
|
||||
if alternate is not None:
|
||||
self.emit(alternate)
|
||||
# end of if statement
|
||||
self.emit('LABEL', end)
|
||||
|
||||
def LabeledStatement(self, label, body, **kwargs):
|
||||
label = label['name']
|
||||
if body['type'] in ('WhileStatement', 'DoWhileStatement',
|
||||
'ForStatement', 'ForInStatement'):
|
||||
# Continue label available... Simply take labels defined by the loop.
|
||||
# It is important that they request continue label first
|
||||
self.declared_continue_labels[label] = self.exe._label_count + 1
|
||||
self.declared_break_labels[label] = self.exe._label_count + 2
|
||||
self.emit(body)
|
||||
del self.declared_break_labels[label]
|
||||
del self.declared_continue_labels[label]
|
||||
else:
|
||||
# only break label available
|
||||
lbl = self.exe.get_new_label()
|
||||
self.declared_break_labels[label] = lbl
|
||||
self.emit(body)
|
||||
self.emit('LABEL', lbl)
|
||||
del self.declared_break_labels[label]
|
||||
|
||||
def Literal(self, value, **kwargs):
|
||||
if value is None:
|
||||
self.emit('LOAD_NULL')
|
||||
elif isinstance(value, bool):
|
||||
self.emit('LOAD_BOOLEAN', int(value))
|
||||
elif isinstance(value, basestring):
|
||||
self.emit('LOAD_STRING', unicode(value))
|
||||
elif isinstance(value, (float, int, long)):
|
||||
self.emit('LOAD_NUMBER', float(value))
|
||||
elif isinstance(value, tuple):
|
||||
self.emit('LOAD_REGEXP', *value)
|
||||
else:
|
||||
raise RuntimeError('Unsupported literal')
|
||||
|
||||
def LogicalExpression(self, left, right, operator, **kwargs):
|
||||
end = self.exe.get_new_label()
|
||||
if operator == '&&':
|
||||
# AND
|
||||
self.emit(left)
|
||||
self.emit('JUMP_IF_FALSE_WITHOUT_POP', end)
|
||||
self.emit('POP')
|
||||
self.emit(right)
|
||||
self.emit('LABEL', end)
|
||||
elif operator == '||':
|
||||
# OR
|
||||
self.emit(left)
|
||||
self.emit('JUMP_IF_TRUE_WITHOUT_POP', end)
|
||||
self.emit('POP')
|
||||
self.emit(right)
|
||||
self.emit('LABEL', end)
|
||||
else:
|
||||
raise RuntimeError("Unknown logical expression: %s" % operator)
|
||||
|
||||
def MemberExpression(self, computed, object, property, **kwargs):
|
||||
if computed:
|
||||
self.emit(object)
|
||||
self.emit(property)
|
||||
self.emit('LOAD_MEMBER')
|
||||
else:
|
||||
self.emit(object)
|
||||
self.emit('LOAD_MEMBER_DOT', property['name'])
|
||||
|
||||
def NewExpression(self, callee, arguments, **kwargs):
|
||||
self.emit(callee)
|
||||
if arguments:
|
||||
n = len(arguments)
|
||||
for e in arguments:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_N_TUPLE', n)
|
||||
self.emit('NEW')
|
||||
else:
|
||||
self.emit('NEW_NO_ARGS')
|
||||
|
||||
def ObjectExpression(self, properties, **kwargs):
|
||||
data = []
|
||||
for prop in properties:
|
||||
self.emit(prop['value'])
|
||||
if prop['computed']:
|
||||
raise NotImplementedError(
|
||||
'ECMA 5.1 does not support computed object properties!')
|
||||
data.append((to_key(prop['key']), prop['kind'][0]))
|
||||
self.emit('LOAD_OBJECT', tuple(data))
|
||||
|
||||
def Program(self, body, **kwargs):
|
||||
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 in ECMA 5.1')
|
||||
|
||||
def RestElement(self, argument, **kwargs):
|
||||
raise NotImplementedError('Not available in ECMA 5.1')
|
||||
|
||||
def ReturnStatement(self, argument, **kwargs):
|
||||
self.emit('POP') # pop result of expression statements
|
||||
if argument is None:
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
else:
|
||||
self.emit(argument)
|
||||
self.emit('RETURN')
|
||||
|
||||
def SequenceExpression(self, expressions, **kwargs):
|
||||
for e in expressions:
|
||||
self.emit(e)
|
||||
self.emit('POP')
|
||||
del self.exe.tape[-1]
|
||||
|
||||
def SwitchCase(self, test, consequent, **kwargs):
|
||||
raise NotImplementedError('Already implemented in SwitchStatement')
|
||||
|
||||
def SwitchStatement(self, discriminant, cases, **kwargs):
|
||||
self.emit(discriminant)
|
||||
labels = [self.exe.get_new_label() for case in cases]
|
||||
tests = [case['test'] for case in cases]
|
||||
consequents = [case['consequent'] for case in cases]
|
||||
end_of_switch = self.exe.get_new_label()
|
||||
|
||||
# translate test cases
|
||||
for test, label in zip(tests, labels):
|
||||
if test is not None:
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_EQ', label)
|
||||
else:
|
||||
self.emit('POP')
|
||||
self.emit('JUMP', label)
|
||||
# this will be executed if none of the cases worked
|
||||
self.emit('POP')
|
||||
self.emit('JUMP', end_of_switch)
|
||||
|
||||
# translate consequents
|
||||
self.implicit_breaks.append(end_of_switch)
|
||||
for consequent, label in zip(consequents, labels):
|
||||
self.emit('LABEL', label)
|
||||
self._emit_statement_list(consequent)
|
||||
self.implicit_breaks.pop()
|
||||
|
||||
self.emit('LABEL', end_of_switch)
|
||||
|
||||
def ThisExpression(self, **kwargs):
|
||||
self.emit('LOAD_THIS')
|
||||
|
||||
def ThrowStatement(self, argument, **kwargs):
|
||||
# throw with the empty stack
|
||||
self.emit('POP')
|
||||
self.emit(argument)
|
||||
self.emit('THROW')
|
||||
|
||||
def TryStatement(self, block, handler, finalizer, **kwargs):
|
||||
try_label = self.exe.get_new_label()
|
||||
catch_label = self.exe.get_new_label()
|
||||
finally_label = self.exe.get_new_label()
|
||||
end_label = self.exe.get_new_label()
|
||||
|
||||
self.emit('JUMP', end_label)
|
||||
|
||||
# try block
|
||||
self.emit('LABEL', try_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
self.emit(block)
|
||||
self.emit(
|
||||
'NOP'
|
||||
) # needed to distinguish from break/continue vs some internal jumps
|
||||
|
||||
# catch block
|
||||
self.emit('LABEL', catch_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
if handler:
|
||||
self.emit(handler['body'])
|
||||
self.emit('NOP')
|
||||
|
||||
# finally block
|
||||
self.emit('LABEL', finally_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
if finalizer:
|
||||
self.emit(finalizer)
|
||||
self.emit('NOP')
|
||||
|
||||
self.emit('LABEL', end_label)
|
||||
|
||||
# give life to the code
|
||||
self.emit('TRY_CATCH_FINALLY', try_label, catch_label,
|
||||
handler['param']['name'] if handler else None, finally_label,
|
||||
bool(finalizer), end_label)
|
||||
|
||||
def UnaryExpression(self, operator, argument, **kwargs):
|
||||
if operator == 'typeof' and argument[
|
||||
'type'] == 'Identifier': # todo fix typeof
|
||||
self.emit('TYPEOF', argument['name'])
|
||||
elif operator == 'delete':
|
||||
if argument['type'] == 'MemberExpression':
|
||||
self.emit(argument['object'])
|
||||
if argument['property']['type'] == 'Identifier':
|
||||
self.emit('LOAD_STRING',
|
||||
unicode(argument['property']['name']))
|
||||
else:
|
||||
self.emit(argument['property'])
|
||||
self.emit('DELETE_MEMBER')
|
||||
elif argument['type'] == 'Identifier':
|
||||
self.emit('DELETE', argument['name'])
|
||||
else:
|
||||
self.emit('LOAD_BOOLEAN', 1)
|
||||
elif operator in UNARY_OPERATIONS:
|
||||
self.emit(argument)
|
||||
self.emit('UNARY_OP', operator)
|
||||
else:
|
||||
raise MakeError('SyntaxError',
|
||||
'Unknown unary operator %s' % operator)
|
||||
|
||||
def UpdateExpression(self, operator, argument, prefix, **kwargs):
|
||||
incr = int(operator == "++")
|
||||
post = int(not prefix)
|
||||
if argument['type'] == 'MemberExpression':
|
||||
if argument['computed']:
|
||||
self.emit(argument['object'])
|
||||
self.emit(argument['property'])
|
||||
self.emit('POSTFIX_MEMBER', post, incr)
|
||||
else:
|
||||
self.emit(argument['object'])
|
||||
name = to_key(argument['property'])
|
||||
self.emit('POSTFIX_MEMBER_DOT', post, incr, name)
|
||||
elif argument['type'] == 'Identifier':
|
||||
name = to_key(argument)
|
||||
self.emit('POSTFIX', post, incr, name)
|
||||
else:
|
||||
raise MakeError('SyntaxError',
|
||||
'Invalid left-hand side in assignment')
|
||||
|
||||
def VariableDeclaration(self, declarations, kind, **kwargs):
|
||||
if kind != 'var':
|
||||
raise NotImplementedError(
|
||||
'Only var variable declaration is supported by ECMA 5.1')
|
||||
for d in declarations:
|
||||
self.emit(d)
|
||||
|
||||
def LexicalDeclaration(self, declarations, kind, **kwargs):
|
||||
raise NotImplementedError('Not supported by ECMA 5.1')
|
||||
|
||||
def VariableDeclarator(self, id, init, **kwargs):
|
||||
name = id['name']
|
||||
if name in SPECIAL_IDENTIFIERS:
|
||||
raise MakeError('Invalid left-hand side in assignment')
|
||||
self.declared_vars.append(name)
|
||||
if init is not None:
|
||||
self.emit(init)
|
||||
self.emit('STORE', name)
|
||||
self.emit('POP')
|
||||
|
||||
def WhileStatement(self, test, body, **kwargs):
|
||||
continue_label = self.exe.get_new_label()
|
||||
break_label = self.exe.get_new_label()
|
||||
|
||||
self.emit('LABEL', continue_label)
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', break_label)
|
||||
|
||||
# translate the body, remember to add and afterwards remove implicit break/continue labels
|
||||
|
||||
self.implicit_continues.append(continue_label)
|
||||
self.implicit_breaks.append(break_label)
|
||||
self.emit(body)
|
||||
self.implicit_continues.pop()
|
||||
self.implicit_breaks.pop()
|
||||
|
||||
self.emit('JUMP', continue_label) # loop back
|
||||
self.emit('LABEL', break_label)
|
||||
|
||||
def WithStatement(self, object, body, **kwargs):
|
||||
beg_label = self.exe.get_new_label()
|
||||
end_label = self.exe.get_new_label()
|
||||
# scope
|
||||
self.emit(object)
|
||||
|
||||
# now the body
|
||||
self.emit('JUMP', end_label)
|
||||
self.emit('LABEL', beg_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
self.emit(body)
|
||||
self.emit('NOP')
|
||||
self.emit('LABEL', end_label)
|
||||
|
||||
# with statement implementation
|
||||
self.emit('WITH', beg_label, end_label)
|
||||
|
||||
def _emit_statement_list(self, statements):
|
||||
for statement in statements:
|
||||
self.emit(statement)
|
||||
|
||||
def emit(self, what, *args):
|
||||
''' what can be either name of the op, or node, or a list of statements.'''
|
||||
if isinstance(what, basestring):
|
||||
return self.exe.emit(what, *args)
|
||||
elif isinstance(what, list):
|
||||
self._emit_statement_list(what)
|
||||
else:
|
||||
return getattr(self, what['type'])(**what)
|
||||
|
||||
|
||||
import os, codecs
|
||||
|
||||
|
||||
def path_as_local(path):
|
||||
if os.path.isabs(path):
|
||||
return path
|
||||
# relative to cwd
|
||||
return os.path.join(os.getcwd(), path)
|
||||
|
||||
|
||||
def get_file_contents(path_or_file):
|
||||
if hasattr(path_or_file, 'read'):
|
||||
js = path_or_file.read()
|
||||
else:
|
||||
with codecs.open(path_as_local(path_or_file), "r", "utf-8") as f:
|
||||
js = f.read()
|
||||
return js
|
||||
|
||||
|
||||
def main():
|
||||
from space import Space
|
||||
import fill_space
|
||||
|
||||
from pyjsparser import parse
|
||||
import json
|
||||
a = ByteCodeGenerator(Code())
|
||||
|
||||
s = Space()
|
||||
fill_space.fill_space(s, a)
|
||||
|
||||
a.exe.space = s
|
||||
s.exe = a.exe
|
||||
con = get_file_contents('internals/esprima.js')
|
||||
d = parse(con + (
|
||||
''';JSON.stringify(exports.parse(%s), 4, 4)''' % json.dumps(con)))
|
||||
# d = parse('''
|
||||
# function x(n) {
|
||||
# log(n)
|
||||
# return x(n+1)
|
||||
# }
|
||||
# x(0)
|
||||
# ''')
|
||||
|
||||
# var v = 333333;
|
||||
# while (v) {
|
||||
# v--
|
||||
#
|
||||
# }
|
||||
a.emit(d)
|
||||
print a.declared_vars
|
||||
print a.exe.tape
|
||||
print len(a.exe.tape)
|
||||
|
||||
a.exe.compile()
|
||||
|
||||
def log(this, args):
|
||||
print args[0]
|
||||
return 999
|
||||
|
||||
print a.exe.run(a.exe.space.GlobalObj)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,197 +0,0 @@
|
||||
from opcodes import *
|
||||
from space import *
|
||||
from base import *
|
||||
|
||||
|
||||
class Code:
|
||||
'''Can generate, store and run sequence of ops representing js code'''
|
||||
|
||||
def __init__(self, is_strict=False):
|
||||
self.tape = []
|
||||
self.compiled = False
|
||||
self.label_locs = None
|
||||
self.is_strict = is_strict
|
||||
|
||||
self.contexts = []
|
||||
self.current_ctx = None
|
||||
self.return_locs = []
|
||||
self._label_count = 0
|
||||
self.label_locs = None
|
||||
|
||||
# useful references
|
||||
self.GLOBAL_THIS = None
|
||||
self.space = None
|
||||
|
||||
def get_new_label(self):
|
||||
self._label_count += 1
|
||||
return self._label_count
|
||||
|
||||
def emit(self, op_code, *args):
|
||||
''' Adds op_code with specified args to tape '''
|
||||
self.tape.append(OP_CODES[op_code](*args))
|
||||
|
||||
def compile(self, start_loc=0):
|
||||
''' Records locations of labels and compiles the code '''
|
||||
self.label_locs = {} if self.label_locs is None else self.label_locs
|
||||
loc = start_loc
|
||||
while loc < len(self.tape):
|
||||
if type(self.tape[loc]) == LABEL:
|
||||
self.label_locs[self.tape[loc].num] = loc
|
||||
del self.tape[loc]
|
||||
continue
|
||||
loc += 1
|
||||
self.compiled = True
|
||||
|
||||
def _call(self, func, this, args):
|
||||
''' Calls a bytecode function func
|
||||
NOTE: use !ONLY! when calling functions from native methods! '''
|
||||
assert not func.is_native
|
||||
# fake call - the the runner to return to the end of the file
|
||||
old_contexts = self.contexts
|
||||
old_return_locs = self.return_locs
|
||||
old_curr_ctx = self.current_ctx
|
||||
|
||||
self.contexts = [FakeCtx()]
|
||||
self.return_locs = [len(self.tape)] # target line after return
|
||||
|
||||
# prepare my ctx
|
||||
my_ctx = func._generate_my_context(this, args)
|
||||
self.current_ctx = my_ctx
|
||||
|
||||
# execute dunction
|
||||
ret = self.run(my_ctx, starting_loc=self.label_locs[func.code])
|
||||
|
||||
# bring back old execution
|
||||
self.current_ctx = old_curr_ctx
|
||||
self.contexts = old_contexts
|
||||
self.return_locs = old_return_locs
|
||||
|
||||
return ret
|
||||
|
||||
def execute_fragment_under_context(self, ctx, start_label, end_label):
|
||||
''' just like run but returns if moved outside of the specified fragment
|
||||
# 4 different exectution results
|
||||
# 0=normal, 1=return, 2=jump_outside, 3=errors
|
||||
# execute_fragment_under_context returns:
|
||||
# (return_value, typ, return_value/jump_loc/py_error)
|
||||
# ctx.stack must be len 1 and its always empty after the call.
|
||||
'''
|
||||
old_curr_ctx = self.current_ctx
|
||||
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[:]
|
||||
return undefined, 3, err
|
||||
finally:
|
||||
self.current_ctx = old_curr_ctx
|
||||
|
||||
def _execute_fragment_under_context(self, ctx, start_label, end_label):
|
||||
start, end = self.label_locs[start_label], self.label_locs[end_label]
|
||||
initial_len = len(ctx.stack)
|
||||
loc = start
|
||||
entry_level = len(self.contexts)
|
||||
# for e in self.tape[start:end]:
|
||||
# print e
|
||||
|
||||
while loc < len(self.tape):
|
||||
#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
|
||||
|
||||
# execute instruction
|
||||
status = self.tape[loc].eval(ctx)
|
||||
|
||||
# check status for special actions
|
||||
if status is not None:
|
||||
if type(status) == int: # jump to label
|
||||
loc = self.label_locs[status]
|
||||
if len(self.contexts) == entry_level:
|
||||
# check if jumped outside of the fragment and break if so
|
||||
if not start <= loc < end:
|
||||
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!
|
||||
# call: (new_ctx, func_loc_label_num)
|
||||
if status[0] is not None:
|
||||
# append old state to the stack
|
||||
self.contexts.append(ctx)
|
||||
self.return_locs.append(loc + 1)
|
||||
# set new state
|
||||
loc = self.label_locs[status[1]]
|
||||
ctx = status[0]
|
||||
self.current_ctx = ctx
|
||||
continue
|
||||
|
||||
# return: (None, None)
|
||||
else:
|
||||
if len(self.contexts) == entry_level:
|
||||
assert len(ctx.stack) == 1 + initial_len
|
||||
return undefined, 1, ctx.stack.pop(
|
||||
) # return signal
|
||||
return_value = ctx.stack.pop()
|
||||
ctx = self.contexts.pop()
|
||||
self.current_ctx = ctx
|
||||
ctx.stack.append(return_value)
|
||||
|
||||
loc = self.return_locs.pop()
|
||||
continue
|
||||
# next instruction
|
||||
loc += 1
|
||||
assert False, 'Remember to add NOP at the end!'
|
||||
|
||||
def run(self, ctx, starting_loc=0):
|
||||
loc = starting_loc
|
||||
self.current_ctx = ctx
|
||||
while loc < len(self.tape):
|
||||
# execute instruction
|
||||
#print loc, self.tape[loc]
|
||||
status = self.tape[loc].eval(ctx)
|
||||
|
||||
# check status for special actions
|
||||
if status is not None:
|
||||
if type(status) == int: # jump to label
|
||||
loc = self.label_locs[status]
|
||||
continue
|
||||
|
||||
elif len(status) == 2: # a call or a return!
|
||||
# call: (new_ctx, func_loc_label_num)
|
||||
if status[0] is not None:
|
||||
# append old state to the stack
|
||||
self.contexts.append(ctx)
|
||||
self.return_locs.append(loc + 1)
|
||||
# set new state
|
||||
loc = self.label_locs[status[1]]
|
||||
ctx = status[0]
|
||||
self.current_ctx = ctx
|
||||
continue
|
||||
|
||||
# return: (None, None)
|
||||
else:
|
||||
return_value = ctx.stack.pop()
|
||||
ctx = self.contexts.pop()
|
||||
self.current_ctx = ctx
|
||||
ctx.stack.append(return_value)
|
||||
|
||||
loc = self.return_locs.pop()
|
||||
continue
|
||||
# next instruction
|
||||
loc += 1
|
||||
assert len(ctx.stack) == 1, ctx.stack
|
||||
return ctx.stack.pop()
|
||||
|
||||
|
||||
class FakeCtx(object):
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
@@ -1 +0,0 @@
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
@@ -1,28 +0,0 @@
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
def Array(this, args):
|
||||
return ArrayConstructor(args, args.space)
|
||||
|
||||
|
||||
def ArrayConstructor(args, space):
|
||||
if len(args) == 1:
|
||||
l = get_arg(args, 0)
|
||||
if type(l) == float:
|
||||
if to_uint32(l) == l:
|
||||
return space.NewArray(l)
|
||||
else:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Invalid length specified for Array constructor (must be uint32)'
|
||||
)
|
||||
else:
|
||||
return space.ConstructArray([l])
|
||||
else:
|
||||
return space.ConstructArray(list(args))
|
||||
|
||||
|
||||
def isArray(this, args):
|
||||
x = get_arg(args, 0)
|
||||
return is_object(x) and x.Class == u'Array'
|
||||
@@ -1,14 +0,0 @@
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
def Boolean(this, args):
|
||||
return to_boolean(get_arg(args, 0))
|
||||
|
||||
|
||||
def BooleanConstructor(args, space):
|
||||
temp = space.NewObject()
|
||||
temp.prototype = space.BooleanPrototype
|
||||
temp.Class = 'Boolean'
|
||||
temp.value = to_boolean(get_arg(args, 0))
|
||||
return temp
|
||||
@@ -1,11 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from js2py.internals.conversions import *
|
||||
from js2py.internals.func_utils import *
|
||||
|
||||
|
||||
class ConsoleMethods:
|
||||
def log(this, args):
|
||||
x = ' '.join(to_string(e) for e in args)
|
||||
print(x)
|
||||
return undefined
|
||||
@@ -1,405 +0,0 @@
|
||||
from ..base import *
|
||||
from .time_helpers import *
|
||||
|
||||
TZ_OFFSET = (time.altzone // 3600)
|
||||
ABS_OFFSET = abs(TZ_OFFSET)
|
||||
TZ_NAME = time.tzname[1]
|
||||
ISO_FORMAT = '%s-%s-%sT%s:%s:%s.%sZ'
|
||||
|
||||
|
||||
@Js
|
||||
def Date(year, month, date, hours, minutes, seconds, ms):
|
||||
return now().to_string()
|
||||
|
||||
|
||||
Date.Class = 'Date'
|
||||
|
||||
|
||||
def now():
|
||||
return PyJsDate(int(time.time() * 1000), prototype=DatePrototype)
|
||||
|
||||
|
||||
@Js
|
||||
def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this
|
||||
args = arguments
|
||||
y = args[0].to_number()
|
||||
m = args[1].to_number()
|
||||
l = len(args)
|
||||
dt = args[2].to_number() if l > 2 else Js(1)
|
||||
h = args[3].to_number() if l > 3 else Js(0)
|
||||
mi = args[4].to_number() if l > 4 else Js(0)
|
||||
sec = args[5].to_number() if l > 5 else Js(0)
|
||||
mili = args[6].to_number() if l > 6 else Js(0)
|
||||
if not y.is_nan() and 0 <= y.value <= 99:
|
||||
y = y + Js(1900)
|
||||
t = TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
|
||||
return PyJsDate(t, prototype=DatePrototype)
|
||||
|
||||
|
||||
@Js
|
||||
def parse(string):
|
||||
return PyJsDate(
|
||||
TimeClip(parse_date(string.to_string().value)),
|
||||
prototype=DatePrototype)
|
||||
|
||||
|
||||
Date.define_own_property('now', {
|
||||
'value': Js(now),
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Date.define_own_property('parse', {
|
||||
'value': parse,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Date.define_own_property('UTC', {
|
||||
'value': UTC,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
|
||||
class PyJsDate(PyJs):
|
||||
Class = 'Date'
|
||||
extensible = True
|
||||
|
||||
def __init__(self, value, prototype=None):
|
||||
self.value = value
|
||||
self.own = {}
|
||||
self.prototype = prototype
|
||||
|
||||
# todo fix this problematic datetime part
|
||||
def to_local_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(
|
||||
UTCToLocal(self.value) // 1000)
|
||||
|
||||
def to_utc_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(self.value // 1000)
|
||||
|
||||
def local_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_local_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
def utc_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_utc_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
|
||||
def parse_date(py_string): # todo support all date string formats
|
||||
try:
|
||||
try:
|
||||
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
except:
|
||||
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%SZ")
|
||||
return MakeDate(
|
||||
MakeDay(Js(dt.year), Js(dt.month - 1), Js(dt.day)),
|
||||
MakeTime(
|
||||
Js(dt.hour), Js(dt.minute), Js(dt.second),
|
||||
Js(dt.microsecond // 1000)))
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not parse date %s - unsupported date format. Currently only supported format is RFC3339 utc. Sorry!'
|
||||
% py_string)
|
||||
|
||||
|
||||
def date_constructor(*args):
|
||||
if len(args) >= 2:
|
||||
return date_constructor2(*args)
|
||||
elif len(args) == 1:
|
||||
return date_constructor1(args[0])
|
||||
else:
|
||||
return date_constructor0()
|
||||
|
||||
|
||||
def date_constructor0():
|
||||
return now()
|
||||
|
||||
|
||||
def date_constructor1(value):
|
||||
v = value.to_primitive()
|
||||
if v._type() == 'String':
|
||||
v = parse_date(v.value)
|
||||
else:
|
||||
v = v.to_int()
|
||||
return PyJsDate(TimeClip(v), prototype=DatePrototype)
|
||||
|
||||
|
||||
def date_constructor2(*args):
|
||||
y = args[0].to_number()
|
||||
m = args[1].to_number()
|
||||
l = len(args)
|
||||
dt = args[2].to_number() if l > 2 else Js(1)
|
||||
h = args[3].to_number() if l > 3 else Js(0)
|
||||
mi = args[4].to_number() if l > 4 else Js(0)
|
||||
sec = args[5].to_number() if l > 5 else Js(0)
|
||||
mili = args[6].to_number() if l > 6 else Js(0)
|
||||
if not y.is_nan() and 0 <= y.value <= 99:
|
||||
y = y + Js(1900)
|
||||
t = TimeClip(
|
||||
LocalToUTC(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili))))
|
||||
return PyJsDate(t, prototype=DatePrototype)
|
||||
|
||||
|
||||
Date.create = date_constructor
|
||||
|
||||
DatePrototype = PyJsDate(float('nan'), prototype=ObjectPrototype)
|
||||
|
||||
|
||||
def check_date(obj):
|
||||
if obj.Class != 'Date':
|
||||
raise MakeError('TypeError', 'this is not a Date object')
|
||||
|
||||
|
||||
class DateProto:
|
||||
def toString():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return 'Invalid Date'
|
||||
offset = (UTCToLocal(this.value) - this.value) // msPerHour
|
||||
return this.local_strftime(
|
||||
'%a %b %d %Y %H:%M:%S GMT') + '%s00 (%s)' % (pad(
|
||||
offset, 2, True), GetTimeZoneName(this.value))
|
||||
|
||||
def toDateString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y')
|
||||
|
||||
def toTimeString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%H:%M:%S')
|
||||
|
||||
def toLocaleString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y %H:%M:%S')
|
||||
|
||||
def toLocaleDateString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y')
|
||||
|
||||
def toLocaleTimeString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%H:%M:%S')
|
||||
|
||||
def valueOf():
|
||||
check_date(this)
|
||||
return this.value
|
||||
|
||||
def getTime():
|
||||
check_date(this)
|
||||
return this.value
|
||||
|
||||
def getFullYear():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return YearFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCFullYear():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return YearFromTime(this.value)
|
||||
|
||||
def getMonth():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MonthFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getDate():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return DateFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMonth():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MonthFromTime(this.value)
|
||||
|
||||
def getUTCDate():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return DateFromTime(this.value)
|
||||
|
||||
def getDay():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return WeekDay(UTCToLocal(this.value))
|
||||
|
||||
def getUTCDay():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return WeekDay(this.value)
|
||||
|
||||
def getHours():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return HourFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCHours():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return HourFromTime(this.value)
|
||||
|
||||
def getMinutes():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MinFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMinutes():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MinFromTime(this.value)
|
||||
|
||||
def getSeconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return SecFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCSeconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return SecFromTime(this.value)
|
||||
|
||||
def getMilliseconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return msFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMilliseconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return msFromTime(this.value)
|
||||
|
||||
def getTimezoneOffset():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return (this.value - UTCToLocal(this.value)) // 60000
|
||||
|
||||
def setTime(time):
|
||||
check_date(this)
|
||||
this.value = TimeClip(time.to_number().to_int())
|
||||
return this.value
|
||||
|
||||
def setMilliseconds(ms):
|
||||
check_date(this)
|
||||
t = UTCToLocal(this.value)
|
||||
tim = MakeTime(
|
||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
||||
u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
|
||||
this.value = u
|
||||
return u
|
||||
|
||||
def setUTCMilliseconds(ms):
|
||||
check_date(this)
|
||||
t = this.value
|
||||
tim = MakeTime(
|
||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
||||
u = TimeClip(MakeDate(Day(t), tim))
|
||||
this.value = u
|
||||
return u
|
||||
|
||||
# todo Complete all setters!
|
||||
|
||||
def toUTCString():
|
||||
check_date(this)
|
||||
return this.utc_strftime('%d %B %Y %H:%M:%S')
|
||||
|
||||
def toISOString():
|
||||
check_date(this)
|
||||
t = this.value
|
||||
year = YearFromTime(t)
|
||||
month, day, hour, minute, second, milli = pad(
|
||||
MonthFromTime(t) + 1), pad(DateFromTime(t)), pad(
|
||||
HourFromTime(t)), pad(MinFromTime(t)), pad(
|
||||
SecFromTime(t)), pad(msFromTime(t))
|
||||
return ISO_FORMAT % (unicode(year) if 0 <= year <= 9999 else pad(
|
||||
year, 6, True), month, day, hour, minute, second, milli)
|
||||
|
||||
def toJSON(key):
|
||||
o = this.to_object()
|
||||
tv = o.to_primitive('Number')
|
||||
if tv.Class == 'Number' and not tv.is_finite():
|
||||
return this.null
|
||||
toISO = o.get('toISOString')
|
||||
if not toISO.is_callable():
|
||||
raise this.MakeError('TypeError', 'toISOString is not callable')
|
||||
return toISO.call(o, ())
|
||||
|
||||
|
||||
def pad(num, n=2, sign=False):
|
||||
'''returns n digit string representation of the num'''
|
||||
s = unicode(abs(num))
|
||||
if len(s) < n:
|
||||
s = '0' * (n - len(s)) + s
|
||||
if not sign:
|
||||
return s
|
||||
if num >= 0:
|
||||
return '+' + s
|
||||
else:
|
||||
return '-' + s
|
||||
|
||||
|
||||
fill_prototype(DatePrototype, DateProto, default_attrs)
|
||||
|
||||
Date.define_own_property(
|
||||
'prototype', {
|
||||
'value': DatePrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
DatePrototype.define_own_property('constructor', {
|
||||
'value': Date,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,75 +0,0 @@
|
||||
from ..base import *
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from pyjsparser import parse
|
||||
from ..byte_trans import ByteCodeGenerator, Code
|
||||
|
||||
|
||||
def Function(this, args):
|
||||
# convert arguments to python list of strings
|
||||
a = map(to_string, tuple(args))
|
||||
_body = u';'
|
||||
_args = ()
|
||||
if len(a):
|
||||
_body = u'%s;' % a[-1]
|
||||
_args = a[:-1]
|
||||
return executable_function(_body, _args, args.space, global_context=True)
|
||||
|
||||
|
||||
def executable_function(_body, _args, space, global_context=True):
|
||||
func_str = u'(function (%s) { ; %s ; });' % (u', '.join(_args), _body)
|
||||
|
||||
co = executable_code(
|
||||
code_str=func_str, space=space, global_context=global_context)
|
||||
return co()
|
||||
|
||||
|
||||
# you can use this one lovely piece of function to compile and execute code on the fly! Watch out though as it may generate lots of code.
|
||||
# todo tape cleanup? we dont know which pieces are needed and which are not so rather impossible without smarter machinery something like GC,
|
||||
# a one solution would be to have a separate tape for functions
|
||||
def executable_code(code_str, space, global_context=True):
|
||||
# parse first to check if any SyntaxErrors
|
||||
parsed = parse(code_str)
|
||||
|
||||
old_tape_len = len(space.byte_generator.exe.tape)
|
||||
space.byte_generator.record_state()
|
||||
start = space.byte_generator.exe.get_new_label()
|
||||
skip = space.byte_generator.exe.get_new_label()
|
||||
space.byte_generator.emit('JUMP', skip)
|
||||
space.byte_generator.emit('LABEL', start)
|
||||
space.byte_generator.emit(parsed)
|
||||
space.byte_generator.emit('NOP')
|
||||
space.byte_generator.emit('LABEL', skip)
|
||||
space.byte_generator.emit('NOP')
|
||||
space.byte_generator.restore_state()
|
||||
space.byte_generator.exe.compile(
|
||||
start_loc=old_tape_len
|
||||
) # dont read the code from the beginning, dont be stupid!
|
||||
|
||||
ctx = space.GlobalObj if global_context else space.exe.current_ctx
|
||||
|
||||
def ex_code():
|
||||
ret, status, token = space.byte_generator.exe.execute_fragment_under_context(
|
||||
ctx, start, skip)
|
||||
# todo Clean up the tape!
|
||||
# this is NOT a way to do that because the fragment may contain the executable code! We dont want to remove it
|
||||
#del space.byte_generator.exe.tape[old_tape_len:]
|
||||
if status == 0:
|
||||
return ret
|
||||
elif status == 3:
|
||||
raise token
|
||||
else:
|
||||
raise RuntimeError(
|
||||
'Unexpected return status during JIT execution: %d' % status)
|
||||
|
||||
return ex_code
|
||||
|
||||
|
||||
def _eval(this, args):
|
||||
code_str = to_string(get_arg(args, 0))
|
||||
return executable_code(code_str, args.space, global_context=True)()
|
||||
|
||||
|
||||
def log(this, args):
|
||||
print ' '.join(map(to_string, args))
|
||||
return undefined
|
||||
@@ -1,157 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
import math
|
||||
import random
|
||||
|
||||
CONSTANTS = {
|
||||
'E': 2.7182818284590452354,
|
||||
'LN10': 2.302585092994046,
|
||||
'LN2': 0.6931471805599453,
|
||||
'LOG2E': 1.4426950408889634,
|
||||
'LOG10E': 0.4342944819032518,
|
||||
'PI': 3.1415926535897932,
|
||||
'SQRT1_2': 0.7071067811865476,
|
||||
'SQRT2': 1.4142135623730951
|
||||
}
|
||||
|
||||
|
||||
class MathFunctions:
|
||||
def abs(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return abs(a)
|
||||
|
||||
def acos(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.acos(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def asin(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.asin(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def atan(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.atan(a)
|
||||
|
||||
def atan2(this, args):
|
||||
x = get_arg(args, 0)
|
||||
y = get_arg(args, 1)
|
||||
a = to_number(x)
|
||||
b = to_number(y)
|
||||
if a != a or b != b: # it must be a nan
|
||||
return NaN
|
||||
return math.atan2(a, b)
|
||||
|
||||
def ceil(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return float(math.ceil(a))
|
||||
|
||||
def floor(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return float(math.floor(a))
|
||||
|
||||
def round(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return float(round(a))
|
||||
|
||||
def sin(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if not is_finite(a): # it must be a nan
|
||||
return NaN
|
||||
return math.sin(a)
|
||||
|
||||
def cos(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if not is_finite(a): # it must be a nan
|
||||
return NaN
|
||||
return math.cos(a)
|
||||
|
||||
def tan(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if not is_finite(a): # it must be a nan
|
||||
return NaN
|
||||
return math.tan(a)
|
||||
|
||||
def log(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.log(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def exp(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.exp(a)
|
||||
|
||||
def pow(this, args):
|
||||
x = get_arg(args, 0)
|
||||
y = get_arg(args, 1)
|
||||
a = to_number(x)
|
||||
b = to_number(y)
|
||||
if a != a or b != b: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return a**b
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def sqrt(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return a**0.5
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def min(this, args):
|
||||
if len(args) == 0:
|
||||
return Infinity
|
||||
return min(map(to_number, tuple(args)))
|
||||
|
||||
def max(this, args):
|
||||
if len(args) == 0:
|
||||
return -Infinity
|
||||
return max(map(to_number, tuple(args)))
|
||||
|
||||
def random(this, args):
|
||||
return random.random()
|
||||
@@ -1,27 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
def Number(this, args):
|
||||
if len(args) == 0:
|
||||
return 0.
|
||||
return to_number(args[0])
|
||||
|
||||
|
||||
def NumberConstructor(args, space):
|
||||
temp = space.NewObject()
|
||||
temp.prototype = space.NumberPrototype
|
||||
temp.Class = 'Number'
|
||||
temp.value = float(to_number(get_arg(args, 0)) if len(args) > 0 else 0.)
|
||||
return temp
|
||||
|
||||
|
||||
CONSTS = {
|
||||
'MAX_VALUE': 1.7976931348623157e308,
|
||||
'MIN_VALUE': 5.0e-324,
|
||||
'NaN': NaN,
|
||||
'NEGATIVE_INFINITY': Infinity,
|
||||
'POSITIVE_INFINITY': -Infinity
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from ..base import is_data_descriptor
|
||||
import six
|
||||
|
||||
|
||||
def Object(this, args):
|
||||
val = get_arg(args, 0)
|
||||
if is_null(val) or is_undefined(val):
|
||||
return args.space.NewObject()
|
||||
return to_object(val, args.space)
|
||||
|
||||
|
||||
def ObjectCreate(args, space):
|
||||
if len(args):
|
||||
val = get_arg(args, 0)
|
||||
if is_object(val):
|
||||
# Implementation dependent, but my will simply return :)
|
||||
return val
|
||||
elif type(val) in (NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE):
|
||||
return to_object(val, space)
|
||||
return space.NewObject()
|
||||
|
||||
|
||||
class ObjectMethods:
|
||||
def getPrototypeOf(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.getPrototypeOf called on non-object')
|
||||
return null if obj.prototype is None else obj.prototype
|
||||
|
||||
def getOwnPropertyDescriptor(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
prop = get_arg(args, 1)
|
||||
if not is_object(obj):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Object.getOwnPropertyDescriptor called on non-object')
|
||||
desc = obj.own.get(to_string(prop))
|
||||
return convert_to_js_type(desc, args.space)
|
||||
|
||||
def getOwnPropertyNames(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Object.getOwnPropertyDescriptor called on non-object')
|
||||
return args.space.ConstructArray(obj.own.keys())
|
||||
|
||||
def create(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not (is_object(obj) or is_null(obj)):
|
||||
raise MakeError('TypeError',
|
||||
'Object prototype may only be an Object or null')
|
||||
temp = args.space.NewObject()
|
||||
temp.prototype = None if is_null(obj) else obj
|
||||
if len(args) > 1 and not is_undefined(args[1]):
|
||||
if six.PY2:
|
||||
args.tup = (args[1], )
|
||||
ObjectMethods.defineProperties.__func__(temp, args)
|
||||
else:
|
||||
args.tup = (args[1], )
|
||||
ObjectMethods.defineProperties(temp, args)
|
||||
return temp
|
||||
|
||||
def defineProperty(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
prop = get_arg(args, 1)
|
||||
attrs = get_arg(args, 2)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.defineProperty called on non-object')
|
||||
name = to_string(prop)
|
||||
if not obj.define_own_property(name, ToPropertyDescriptor(attrs),
|
||||
False):
|
||||
raise MakeError('TypeError', 'Cannot redefine property: %s' % name)
|
||||
return obj
|
||||
|
||||
def defineProperties(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
properties = get_arg(args, 1)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.defineProperties called on non-object')
|
||||
props = to_object(properties, args.space)
|
||||
for k, v in props.own.items():
|
||||
if not v.get('enumerable'):
|
||||
continue
|
||||
desc = ToPropertyDescriptor(props.get(unicode(k)))
|
||||
if not obj.define_own_property(unicode(k), desc, False):
|
||||
raise MakeError('TypeError',
|
||||
'Failed to define own property: %s' % k)
|
||||
return obj
|
||||
|
||||
def seal(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError', 'Object.seal called on non-object')
|
||||
for desc in obj.own.values():
|
||||
desc['configurable'] = False
|
||||
obj.extensible = False
|
||||
return obj
|
||||
|
||||
def freeze(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError', 'Object.freeze called on non-object')
|
||||
for desc in obj.own.values():
|
||||
desc['configurable'] = False
|
||||
if is_data_descriptor(desc):
|
||||
desc['writable'] = False
|
||||
obj.extensible = False
|
||||
return obj
|
||||
|
||||
def preventExtensions(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.preventExtensions on non-object')
|
||||
obj.extensible = False
|
||||
return obj
|
||||
|
||||
def isSealed(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.isSealed called on non-object')
|
||||
if obj.extensible:
|
||||
return False
|
||||
for desc in obj.own.values():
|
||||
if desc.get('configurable'):
|
||||
return False
|
||||
return True
|
||||
|
||||
def isFrozen(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.isFrozen called on non-object')
|
||||
if obj.extensible:
|
||||
return False
|
||||
for desc in obj.own.values():
|
||||
if desc.get('configurable'):
|
||||
return False
|
||||
if is_data_descriptor(desc) and desc.get('writable'):
|
||||
return False
|
||||
return True
|
||||
|
||||
def isExtensible(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.isExtensible called on non-object')
|
||||
return obj.extensible
|
||||
|
||||
def keys(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError', 'Object.keys called on non-object')
|
||||
return args.space.ConstructArray([
|
||||
unicode(e) for e, d in six.iteritems(obj.own)
|
||||
if d.get('enumerable')
|
||||
])
|
||||
|
||||
|
||||
# some utility functions:
|
||||
|
||||
|
||||
def ToPropertyDescriptor(obj): # page 38 (50 absolute)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Can\'t convert non-object to property descriptor')
|
||||
desc = {}
|
||||
if obj.has_property('enumerable'):
|
||||
desc['enumerable'] = to_boolean(obj.get('enumerable'))
|
||||
if obj.has_property('configurable'):
|
||||
desc['configurable'] = to_boolean(obj.get('configurable'))
|
||||
if obj.has_property('value'):
|
||||
desc['value'] = obj.get('value')
|
||||
if obj.has_property('writable'):
|
||||
desc['writable'] = to_boolean(obj.get('writable'))
|
||||
if obj.has_property('get'):
|
||||
cand = obj.get('get')
|
||||
if not (is_undefined(cand) or is_callable(cand)):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Invalid getter (it has to be a function or undefined)')
|
||||
desc['get'] = cand
|
||||
if obj.has_property('set'):
|
||||
cand = obj.get('set')
|
||||
if not (is_undefined(cand) or is_callable(cand)):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Invalid setter (it has to be a function or undefined)')
|
||||
desc['set'] = cand
|
||||
if ('get' in desc or 'set' in desc) and ('value' in desc
|
||||
or 'writable' in desc):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Invalid property. A property cannot both have accessors and be writable or have a value.'
|
||||
)
|
||||
return desc
|
||||
@@ -1,41 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from ..base import SpaceTuple
|
||||
|
||||
REG_EXP_FLAGS = ('g', 'i', 'm')
|
||||
|
||||
|
||||
def RegExp(this, args):
|
||||
pattern = get_arg(args, 0)
|
||||
flags = get_arg(args, 1)
|
||||
if GetClass(pattern) == 'RegExp':
|
||||
if not is_undefined(flags):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Cannot supply flags when constructing one RegExp from another'
|
||||
)
|
||||
# return unchanged
|
||||
return pattern
|
||||
#pattern is not a regexp
|
||||
if is_undefined(pattern):
|
||||
pattern = u''
|
||||
else:
|
||||
pattern = to_string(pattern)
|
||||
flags = to_string(flags) if not is_undefined(flags) else u''
|
||||
for flag in flags:
|
||||
if flag not in REG_EXP_FLAGS:
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid flags supplied to RegExp constructor "%s"' % flag)
|
||||
if len(set(flags)) != len(flags):
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid flags supplied to RegExp constructor "%s"' % flags)
|
||||
return args.space.NewRegExp(pattern, flags)
|
||||
|
||||
|
||||
def RegExpCreate(args, space):
|
||||
_args = SpaceTuple(args)
|
||||
_args.space = space
|
||||
return RegExp(undefined, _args)
|
||||
@@ -1,23 +0,0 @@
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
def fromCharCode(this, args):
|
||||
res = u''
|
||||
for e in args:
|
||||
res += unichr(to_uint16(e))
|
||||
return res
|
||||
|
||||
|
||||
def String(this, args):
|
||||
if len(args) == 0:
|
||||
return u''
|
||||
return to_string(args[0])
|
||||
|
||||
|
||||
def StringConstructor(args, space):
|
||||
temp = space.NewObject()
|
||||
temp.prototype = space.StringPrototype
|
||||
temp.Class = 'String'
|
||||
temp.value = to_string(get_arg(args, 0)) if len(args) > 0 else u''
|
||||
return temp
|
||||
@@ -1,209 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# NOTE: t must be INT!!!
|
||||
import time
|
||||
import datetime
|
||||
import warnings
|
||||
|
||||
try:
|
||||
from tzlocal import get_localzone
|
||||
LOCAL_ZONE = get_localzone()
|
||||
except: # except all problems...
|
||||
warnings.warn(
|
||||
'Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time'
|
||||
)
|
||||
|
||||
class LOCAL_ZONE:
|
||||
@staticmethod
|
||||
def dst(*args):
|
||||
return 1
|
||||
|
||||
|
||||
from js2py.base import MakeError
|
||||
CUM = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
|
||||
msPerDay = 86400000
|
||||
msPerYear = int(86400000 * 365.242)
|
||||
msPerSecond = 1000
|
||||
msPerMinute = 60000
|
||||
msPerHour = 3600000
|
||||
HoursPerDay = 24
|
||||
MinutesPerHour = 60
|
||||
SecondsPerMinute = 60
|
||||
NaN = float('nan')
|
||||
LocalTZA = -time.timezone * msPerSecond
|
||||
|
||||
|
||||
def DaylightSavingTA(t):
|
||||
if t is NaN:
|
||||
return t
|
||||
try:
|
||||
return int(
|
||||
LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(
|
||||
t // 1000)).seconds) * 1000
|
||||
except:
|
||||
warnings.warn(
|
||||
'Invalid datetime date, assumed DST time, may be inaccurate...',
|
||||
Warning)
|
||||
return 1
|
||||
#raise MakeError('TypeError', 'date not supported by python.datetime. I will solve it in future versions')
|
||||
|
||||
|
||||
def GetTimeZoneName(t):
|
||||
return time.tzname[DaylightSavingTA(t) > 0]
|
||||
|
||||
|
||||
def LocalToUTC(t):
|
||||
return t - LocalTZA - DaylightSavingTA(t - LocalTZA)
|
||||
|
||||
|
||||
def UTCToLocal(t):
|
||||
return t + LocalTZA + DaylightSavingTA(t)
|
||||
|
||||
|
||||
def Day(t):
|
||||
return t // 86400000
|
||||
|
||||
|
||||
def TimeWithinDay(t):
|
||||
return t % 86400000
|
||||
|
||||
|
||||
def DaysInYear(y):
|
||||
if y % 4:
|
||||
return 365
|
||||
elif y % 100:
|
||||
return 366
|
||||
elif y % 400:
|
||||
return 365
|
||||
else:
|
||||
return 366
|
||||
|
||||
|
||||
def DayFromYear(y):
|
||||
return 365 * (y - 1970) + (y - 1969) // 4 - (y - 1901) // 100 + (
|
||||
y - 1601) // 400
|
||||
|
||||
|
||||
def TimeFromYear(y):
|
||||
return 86400000 * DayFromYear(y)
|
||||
|
||||
|
||||
def YearFromTime(t):
|
||||
guess = 1970 - t // 31556908800 # msPerYear
|
||||
gt = TimeFromYear(guess)
|
||||
if gt <= t:
|
||||
while gt <= t:
|
||||
guess += 1
|
||||
gt = TimeFromYear(guess)
|
||||
return guess - 1
|
||||
else:
|
||||
while gt > t:
|
||||
guess -= 1
|
||||
gt = TimeFromYear(guess)
|
||||
return guess
|
||||
|
||||
|
||||
def DayWithinYear(t):
|
||||
return Day(t) - DayFromYear(YearFromTime(t))
|
||||
|
||||
|
||||
def InLeapYear(t):
|
||||
y = YearFromTime(t)
|
||||
if y % 4:
|
||||
return 0
|
||||
elif y % 100:
|
||||
return 1
|
||||
elif y % 400:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
def MonthFromTime(t):
|
||||
day = DayWithinYear(t)
|
||||
leap = InLeapYear(t)
|
||||
if day < 31:
|
||||
return 0
|
||||
day -= leap
|
||||
if day < 59:
|
||||
return 1
|
||||
elif day < 90:
|
||||
return 2
|
||||
elif day < 120:
|
||||
return 3
|
||||
elif day < 151:
|
||||
return 4
|
||||
elif day < 181:
|
||||
return 5
|
||||
elif day < 212:
|
||||
return 6
|
||||
elif day < 243:
|
||||
return 7
|
||||
elif day < 273:
|
||||
return 8
|
||||
elif day < 304:
|
||||
return 9
|
||||
elif day < 334:
|
||||
return 10
|
||||
else:
|
||||
return 11
|
||||
|
||||
|
||||
def DateFromTime(t):
|
||||
mon = MonthFromTime(t)
|
||||
day = DayWithinYear(t)
|
||||
return day - CUM[mon] - (1 if InLeapYear(t) and mon >= 2 else 0) + 1
|
||||
|
||||
|
||||
def WeekDay(t):
|
||||
# 0 == sunday
|
||||
return (Day(t) + 4) % 7
|
||||
|
||||
|
||||
def msFromTime(t):
|
||||
return t % 1000
|
||||
|
||||
|
||||
def SecFromTime(t):
|
||||
return (t // 1000) % 60
|
||||
|
||||
|
||||
def MinFromTime(t):
|
||||
return (t // 60000) % 60
|
||||
|
||||
|
||||
def HourFromTime(t):
|
||||
return (t // 3600000) % 24
|
||||
|
||||
|
||||
def MakeTime(hour, Min, sec, ms):
|
||||
# takes PyJs objects and returns t
|
||||
if not (hour.is_finite() and Min.is_finite() and sec.is_finite()
|
||||
and ms.is_finite()):
|
||||
return NaN
|
||||
h, m, s, milli = hour.to_int(), Min.to_int(), sec.to_int(), ms.to_int()
|
||||
return h * 3600000 + m * 60000 + s * 1000 + milli
|
||||
|
||||
|
||||
def MakeDay(year, month, date):
|
||||
# takes PyJs objects and returns t
|
||||
if not (year.is_finite() and month.is_finite() and date.is_finite()):
|
||||
return NaN
|
||||
y, m, dt = year.to_int(), month.to_int(), date.to_int()
|
||||
y += m // 12
|
||||
mn = m % 12
|
||||
d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y) == 366
|
||||
and mn >= 2 else 0)
|
||||
return d # ms per day
|
||||
|
||||
|
||||
def MakeDate(day, time):
|
||||
return 86400000 * day + time
|
||||
|
||||
|
||||
def TimeClip(t):
|
||||
if t != t or abs(t) == float('inf'):
|
||||
return NaN
|
||||
if abs(t) > 8.64 * 10**15:
|
||||
return NaN
|
||||
return int(t)
|
||||
@@ -1,148 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
# Type Conversions. to_type. All must return PyJs subclass instance
|
||||
from simplex import *
|
||||
|
||||
|
||||
def to_primitive(self, hint=None):
|
||||
if is_primitive(self):
|
||||
return self
|
||||
if hint is None and (self.Class == 'Number' or self.Class == 'Boolean'):
|
||||
# favour number for Class== Number or Boolean default = String
|
||||
hint = 'Number'
|
||||
return self.default_value(hint)
|
||||
|
||||
|
||||
def to_boolean(self):
|
||||
typ = Type(self)
|
||||
if typ == 'Boolean': # no need to convert
|
||||
return self
|
||||
elif typ == 'Null' or typ == 'Undefined': # they are both always false
|
||||
return False
|
||||
elif typ == 'Number': # false only for 0, and NaN
|
||||
return self and self == self # test for nan (nan -> flase)
|
||||
elif typ == 'String':
|
||||
return bool(self)
|
||||
else: # object - always True
|
||||
return True
|
||||
|
||||
|
||||
def to_number(self):
|
||||
typ = Type(self)
|
||||
if typ == 'Number': # or self.Class=='Number': # no need to convert
|
||||
return self
|
||||
elif typ == 'Null': # null is 0
|
||||
return 0.
|
||||
elif typ == 'Undefined': # undefined is NaN
|
||||
return NaN
|
||||
elif typ == 'Boolean': # 1 for True 0 for false
|
||||
return float(self)
|
||||
elif typ == 'String':
|
||||
s = self.strip() # Strip white space
|
||||
if not s: # '' is simply 0
|
||||
return 0.
|
||||
if 'x' in s or 'X' in s[:3]: # hex (positive only)
|
||||
try: # try to convert
|
||||
num = int(s, 16)
|
||||
except ValueError: # could not convert -> NaN
|
||||
return NaN
|
||||
return float(num)
|
||||
sign = 1 # get sign
|
||||
if s[0] in '+-':
|
||||
if s[0] == '-':
|
||||
sign = -1
|
||||
s = s[1:]
|
||||
if s == 'Infinity': # Check for infinity keyword. 'NaN' will be NaN anyway.
|
||||
return sign * Infinity
|
||||
try: # decimal try
|
||||
num = sign * float(s) # Converted
|
||||
except ValueError:
|
||||
return NaN # could not convert to decimal > return NaN
|
||||
return float(num)
|
||||
else: # object - most likely it will be NaN.
|
||||
return to_number(to_primitive(self, 'Number'))
|
||||
|
||||
|
||||
def to_string(self):
|
||||
typ = Type(self)
|
||||
if typ == 'String':
|
||||
return self
|
||||
elif typ == 'Null':
|
||||
return 'null'
|
||||
elif typ == 'Undefined':
|
||||
return 'undefined'
|
||||
elif typ == 'Boolean':
|
||||
return 'true' if self else 'false'
|
||||
elif typ == 'Number': # or self.Class=='Number':
|
||||
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
|
||||
else: # object
|
||||
return to_string(to_primitive(self, 'String'))
|
||||
|
||||
|
||||
def to_object(self, space):
|
||||
typ = Type(self)
|
||||
if typ == 'Object':
|
||||
return self
|
||||
elif typ == 'Boolean': # Unsure ... todo check here
|
||||
return space.Boolean.create((self, ), space)
|
||||
elif typ == 'Number': # ?
|
||||
return space.Number.create((self, ), space)
|
||||
elif typ == 'String': # ?
|
||||
return space.String.create((self, ), space)
|
||||
elif typ == 'Null' or typ == 'Undefined':
|
||||
raise MakeError('TypeError',
|
||||
'undefined or null can\'t be converted to object')
|
||||
else:
|
||||
raise RuntimeError()
|
||||
|
||||
|
||||
def to_int32(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return 0
|
||||
int32 = int(num) % 2**32
|
||||
return int(int32 - 2**32 if int32 >= 2**31 else int32)
|
||||
|
||||
|
||||
def to_int(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num):
|
||||
return 0
|
||||
elif is_infinity(num):
|
||||
return 10**20 if num > 0 else -10**20
|
||||
return int(num)
|
||||
|
||||
|
||||
def to_uint32(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return 0
|
||||
return int(num) % 2**32
|
||||
|
||||
|
||||
def to_uint16(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return 0
|
||||
return int(num) % 2**16
|
||||
|
||||
|
||||
def to_int16(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return 0
|
||||
int16 = int(num) % 2**16
|
||||
return int(int16 - 2**16 if int16 >= 2**15 else int16)
|
||||
|
||||
|
||||
def cok(self):
|
||||
"""Check object coercible"""
|
||||
if type(self) in (UNDEFINED_TYPE, NULL_TYPE):
|
||||
raise MakeError('TypeError',
|
||||
'undefined or null can\'t be converted to object')
|
||||
@@ -1,90 +0,0 @@
|
||||
# todo make sure what they mean by desc undefined? None or empty? Answer: None :) it can never be empty but None is sometimes returned.
|
||||
|
||||
# I am implementing everything as dicts to speed up property creation
|
||||
|
||||
# Warning: value, get, set props of dest are PyJs types. Rest is Py!
|
||||
|
||||
|
||||
def is_data_descriptor(desc):
|
||||
return desc and ('value' in desc or 'writable' in desc)
|
||||
|
||||
|
||||
def is_accessor_descriptor(desc):
|
||||
return desc and ('get' in desc or 'set' in desc)
|
||||
|
||||
|
||||
def is_generic_descriptor(
|
||||
desc
|
||||
): # generic means not the data and not the setter - therefore it must be one that changes only enum and conf
|
||||
return desc and not (is_data_descriptor(desc)
|
||||
or is_accessor_descriptor(desc))
|
||||
|
||||
|
||||
def from_property_descriptor(desc, space):
|
||||
if not desc:
|
||||
return {}
|
||||
obj = space.NewObject()
|
||||
if is_data_descriptor(desc):
|
||||
obj.define_own_property(
|
||||
'value', {
|
||||
'value': desc['value'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
obj.define_own_property(
|
||||
'writable', {
|
||||
'value': desc['writable'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
else:
|
||||
obj.define_own_property(
|
||||
'get', {
|
||||
'value': desc['get'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
obj.define_own_property(
|
||||
'set', {
|
||||
'value': desc['set'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
obj.define_own_property(
|
||||
'writable', {
|
||||
'value': desc['writable'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
obj.define_own_property(
|
||||
'enumerable', {
|
||||
'value': desc['enumerable'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
return obj
|
||||
|
||||
|
||||
def to_property_descriptor(obj):
|
||||
if obj._type() != 'Object':
|
||||
raise TypeError()
|
||||
desc = {}
|
||||
for e in ('enumerable', 'configurable', 'writable'):
|
||||
if obj.has_property(e):
|
||||
desc[e] = obj.get(e).to_boolean().value
|
||||
if obj.has_property('value'):
|
||||
desc['value'] = obj.get('value')
|
||||
for e in ('get', 'set'):
|
||||
if obj.has_property(e):
|
||||
cand = obj.get(e)
|
||||
if not (cand.is_callable() or cand.is_undefined()):
|
||||
raise TypeError()
|
||||
if ('get' in desc or 'set' in desc) and ('value' in desc
|
||||
or 'writable' in desc):
|
||||
raise TypeError()
|
||||
@@ -1,284 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from base import Scope
|
||||
from func_utils import *
|
||||
from conversions import *
|
||||
import six
|
||||
from prototypes.jsboolean import BooleanPrototype
|
||||
from prototypes.jserror import ErrorPrototype
|
||||
from prototypes.jsfunction import FunctionPrototype
|
||||
from prototypes.jsnumber import NumberPrototype
|
||||
from prototypes.jsobject import ObjectPrototype
|
||||
from prototypes.jsregexp import RegExpPrototype
|
||||
from prototypes.jsstring import StringPrototype
|
||||
from prototypes.jsarray import ArrayPrototype
|
||||
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
|
||||
|
||||
|
||||
def fill_proto(proto, proto_class, space):
|
||||
for i in dir(proto_class):
|
||||
e = getattr(proto_class, i)
|
||||
if six.PY2:
|
||||
if hasattr(e, '__func__'):
|
||||
meth = e.__func__
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
if hasattr(e, '__call__') and not i.startswith('__'):
|
||||
meth = e
|
||||
else:
|
||||
continue
|
||||
meth_name = meth.__name__.strip('_') # RexExp._exec -> RegExp.exec
|
||||
js_meth = space.NewFunction(meth, space.ctx, (), meth_name, False, ())
|
||||
set_non_enumerable(proto, meth_name, js_meth)
|
||||
return proto
|
||||
|
||||
|
||||
def easy_func(f, space):
|
||||
return space.NewFunction(f, space.ctx, (), f.__name__, False, ())
|
||||
|
||||
|
||||
def Empty(this, args):
|
||||
return undefined
|
||||
|
||||
|
||||
def set_non_enumerable(obj, name, prop):
|
||||
obj.define_own_property(
|
||||
unicode(name), {
|
||||
'value': prop,
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': True
|
||||
}, True)
|
||||
|
||||
|
||||
def set_protected(obj, name, prop):
|
||||
obj.define_own_property(
|
||||
unicode(name), {
|
||||
'value': prop,
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}, True)
|
||||
|
||||
|
||||
def fill_space(space, byte_generator):
|
||||
# set global scope
|
||||
global_scope = Scope({}, space, parent=None)
|
||||
global_scope.THIS_BINDING = global_scope
|
||||
global_scope.registers(byte_generator.declared_vars)
|
||||
space.GlobalObj = global_scope
|
||||
|
||||
space.byte_generator = byte_generator
|
||||
|
||||
# first init all protos, later take care of constructors and details
|
||||
|
||||
# Function must be first obviously, we have to use a small trick to do that...
|
||||
function_proto = space.NewFunction(Empty, space.ctx, (), 'Empty', False,
|
||||
())
|
||||
space.FunctionPrototype = function_proto # this will fill the prototypes of the methods!
|
||||
fill_proto(function_proto, FunctionPrototype, space)
|
||||
|
||||
# Object next
|
||||
object_proto = space.NewObject() # no proto
|
||||
fill_proto(object_proto, ObjectPrototype, space)
|
||||
space.ObjectPrototype = object_proto
|
||||
function_proto.prototype = object_proto
|
||||
|
||||
# Number
|
||||
number_proto = space.NewObject()
|
||||
number_proto.prototype = object_proto
|
||||
fill_proto(number_proto, NumberPrototype, space)
|
||||
number_proto.value = 0.
|
||||
number_proto.Class = 'Number'
|
||||
space.NumberPrototype = number_proto
|
||||
|
||||
# String
|
||||
string_proto = space.NewObject()
|
||||
string_proto.prototype = object_proto
|
||||
fill_proto(string_proto, StringPrototype, space)
|
||||
string_proto.value = u''
|
||||
string_proto.Class = 'String'
|
||||
space.StringPrototype = string_proto
|
||||
|
||||
# Boolean
|
||||
boolean_proto = space.NewObject()
|
||||
boolean_proto.prototype = object_proto
|
||||
fill_proto(boolean_proto, BooleanPrototype, space)
|
||||
boolean_proto.value = False
|
||||
boolean_proto.Class = 'Boolean'
|
||||
space.BooleanPrototype = boolean_proto
|
||||
|
||||
# Array
|
||||
array_proto = space.NewArray(0)
|
||||
array_proto.prototype = object_proto
|
||||
fill_proto(array_proto, ArrayPrototype, space)
|
||||
space.ArrayPrototype = array_proto
|
||||
|
||||
# JSON
|
||||
json = space.NewObject()
|
||||
json.put(u'stringify', easy_func(jsjson.stringify, space))
|
||||
json.put(u'parse', easy_func(jsjson.parse, space))
|
||||
|
||||
# Utils
|
||||
parseFloat = easy_func(jsutils.parseFloat, space)
|
||||
parseInt = easy_func(jsutils.parseInt, space)
|
||||
isNaN = easy_func(jsutils.isNaN, space)
|
||||
isFinite = easy_func(jsutils.isFinite, space)
|
||||
|
||||
# Error
|
||||
error_proto = space.NewError(u'Error', u'')
|
||||
error_proto.prototype = object_proto
|
||||
error_proto.put(u'name', u'Error')
|
||||
fill_proto(error_proto, ErrorPrototype, space)
|
||||
space.ErrorPrototype = error_proto
|
||||
|
||||
def construct_constructor(typ):
|
||||
def creator(this, args):
|
||||
message = get_arg(args, 0)
|
||||
if not is_undefined(message):
|
||||
msg = to_string(message)
|
||||
else:
|
||||
msg = u''
|
||||
return space.NewError(typ, msg)
|
||||
|
||||
j = easy_func(creator, space)
|
||||
j.name = unicode(typ)
|
||||
j.prototype = space.ERROR_TYPES[typ]
|
||||
|
||||
def new_create(args, space):
|
||||
message = get_arg(args, 0)
|
||||
if not is_undefined(message):
|
||||
msg = to_string(message)
|
||||
else:
|
||||
msg = u''
|
||||
return space.NewError(typ, msg)
|
||||
|
||||
j.create = new_create
|
||||
return j
|
||||
|
||||
# fill remaining error types
|
||||
error_constructors = {}
|
||||
for err_type_name in (u'Error', u'EvalError', u'RangeError',
|
||||
u'ReferenceError', u'SyntaxError', u'TypeError',
|
||||
u'URIError'):
|
||||
extra_err = space.NewError(u'Error', u'')
|
||||
extra_err.put(u'name', err_type_name)
|
||||
setattr(space, err_type_name + u'Prototype', extra_err)
|
||||
error_constructors[err_type_name] = construct_constructor(
|
||||
err_type_name)
|
||||
assert space.TypeErrorPrototype is not None
|
||||
|
||||
# RegExp
|
||||
regexp_proto = space.NewRegExp(u'(?:)', u'')
|
||||
regexp_proto.prototype = object_proto
|
||||
fill_proto(regexp_proto, RegExpPrototype, space)
|
||||
space.RegExpPrototype = regexp_proto
|
||||
|
||||
# Json
|
||||
|
||||
# now all these boring constructors...
|
||||
|
||||
# Number
|
||||
number = easy_func(jsnumber.Number, space)
|
||||
space.Number = number
|
||||
number.create = jsnumber.NumberConstructor
|
||||
set_non_enumerable(number_proto, 'constructor', number)
|
||||
set_protected(number, 'prototype', number_proto)
|
||||
# number has some extra constants
|
||||
for k, v in jsnumber.CONSTS.items():
|
||||
set_protected(number, k, v)
|
||||
|
||||
# String
|
||||
string = easy_func(jsstring.String, space)
|
||||
space.String = string
|
||||
string.create = jsstring.StringConstructor
|
||||
set_non_enumerable(string_proto, 'constructor', string)
|
||||
set_protected(string, 'prototype', string_proto)
|
||||
# string has an extra function
|
||||
set_non_enumerable(string, 'fromCharCode',
|
||||
easy_func(jsstring.fromCharCode, space))
|
||||
|
||||
# Boolean
|
||||
boolean = easy_func(jsboolean.Boolean, space)
|
||||
space.Boolean = boolean
|
||||
boolean.create = jsboolean.BooleanConstructor
|
||||
set_non_enumerable(boolean_proto, 'constructor', boolean)
|
||||
set_protected(boolean, 'prototype', boolean_proto)
|
||||
|
||||
# Array
|
||||
array = easy_func(jsarray.Array, space)
|
||||
space.Array = array
|
||||
array.create = jsarray.ArrayConstructor
|
||||
set_non_enumerable(array_proto, 'constructor', array)
|
||||
set_protected(array, 'prototype', array_proto)
|
||||
array.put(u'isArray', easy_func(jsarray.isArray, space))
|
||||
|
||||
# RegExp
|
||||
regexp = easy_func(jsregexp.RegExp, space)
|
||||
space.RegExp = regexp
|
||||
regexp.create = jsregexp.RegExpCreate
|
||||
set_non_enumerable(regexp_proto, 'constructor', regexp)
|
||||
set_protected(regexp, 'prototype', regexp_proto)
|
||||
|
||||
# Object
|
||||
_object = easy_func(jsobject.Object, space)
|
||||
space.Object = _object
|
||||
_object.create = jsobject.ObjectCreate
|
||||
set_non_enumerable(object_proto, 'constructor', _object)
|
||||
set_protected(_object, 'prototype', object_proto)
|
||||
fill_proto(_object, jsobject.ObjectMethods, space)
|
||||
|
||||
# Function
|
||||
function = easy_func(jsfunction.Function, space)
|
||||
space.Function = function
|
||||
|
||||
# Math
|
||||
math = space.NewObject()
|
||||
math.Class = 'Math'
|
||||
fill_proto(math, jsmath.MathFunctions, space)
|
||||
for k, v in jsmath.CONSTANTS.items():
|
||||
set_protected(math, k, v)
|
||||
|
||||
console = space.NewObject()
|
||||
fill_proto(console, jsconsole.ConsoleMethods, space)
|
||||
|
||||
# set global object
|
||||
builtins = {
|
||||
'String': string,
|
||||
'Number': number,
|
||||
'Boolean': boolean,
|
||||
'RegExp': regexp,
|
||||
'exports': convert_to_js_type({}, space),
|
||||
'Math': math,
|
||||
#'Date',
|
||||
'Object': _object,
|
||||
'Function': function,
|
||||
'JSON': json,
|
||||
'Array': array,
|
||||
'parseFloat': parseFloat,
|
||||
'parseInt': parseInt,
|
||||
'isFinite': isFinite,
|
||||
'isNaN': isNaN,
|
||||
'eval': easy_func(jsfunction._eval, space),
|
||||
'console': console,
|
||||
'log': console.get(u'log'),
|
||||
}
|
||||
|
||||
builtins.update(error_constructors)
|
||||
|
||||
set_protected(global_scope, 'NaN', NaN)
|
||||
set_protected(global_scope, 'Infinity', Infinity)
|
||||
for k, v in builtins.items():
|
||||
set_non_enumerable(global_scope, k, v)
|
||||
@@ -1,73 +0,0 @@
|
||||
from simplex import *
|
||||
from conversions import *
|
||||
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
|
||||
def get_arg(arguments, n):
|
||||
if len(arguments) <= n:
|
||||
return undefined
|
||||
return arguments[n]
|
||||
|
||||
|
||||
def ensure_js_types(args, space=None):
|
||||
return tuple(convert_to_js_type(e, space=space) for e in args)
|
||||
|
||||
|
||||
def convert_to_js_type(e, space=None):
|
||||
t = type(e)
|
||||
if is_js_type(e):
|
||||
return e
|
||||
if t in (int, long, float):
|
||||
return float(e)
|
||||
elif isinstance(t, basestring):
|
||||
return unicode(t)
|
||||
elif t in (list, tuple):
|
||||
if space is None:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Actually an internal error, could not convert to js type because space not specified'
|
||||
)
|
||||
return space.ConstructArray(ensure_js_types(e, space=space))
|
||||
elif t == dict:
|
||||
if space is None:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Actually an internal error, could not convert to js type because space not specified'
|
||||
)
|
||||
new = {}
|
||||
for k, v in e.items():
|
||||
new[to_string(convert_to_js_type(k, space))] = convert_to_js_type(
|
||||
v, space)
|
||||
return space.ConstructObject(new)
|
||||
else:
|
||||
raise MakeError('TypeError', 'Could not convert to js type!')
|
||||
|
||||
|
||||
def is_js_type(e):
|
||||
if type(e) in PRIMITIVES:
|
||||
return True
|
||||
elif hasattr(e, 'Class') and hasattr(e, 'value'): # not perfect but works
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# todo optimise these 2!
|
||||
def js_array_to_tuple(arr):
|
||||
length = to_uint32(arr.get(u'length'))
|
||||
return tuple(arr.get(unicode(e)) for e in xrange(length))
|
||||
|
||||
|
||||
def js_array_to_list(arr):
|
||||
length = to_uint32(arr.get(u'length'))
|
||||
return [arr.get(unicode(e)) for e in xrange(length)]
|
||||
|
||||
|
||||
def js_arr_length(arr):
|
||||
return to_uint32(arr.get(u'length'))
|
||||
@@ -1,805 +0,0 @@
|
||||
from operations import *
|
||||
from base import get_member, get_member_dot, PyJsFunction, Scope
|
||||
|
||||
|
||||
class OP_CODE(object):
|
||||
_params = []
|
||||
|
||||
# def eval(self, ctx):
|
||||
# raise
|
||||
|
||||
def __repr__(self):
|
||||
return self.__class__.__name__ + str(
|
||||
tuple([getattr(self, e) for e in self._params]))
|
||||
|
||||
|
||||
# --------------------- UNARY ----------------------
|
||||
|
||||
|
||||
class UNARY_OP(OP_CODE):
|
||||
_params = ['operator']
|
||||
|
||||
def __init__(self, operator):
|
||||
self.operator = operator
|
||||
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack.pop()
|
||||
ctx.stack.append(UNARY_OPERATIONS[self.operator](val))
|
||||
|
||||
|
||||
# special unary operations
|
||||
|
||||
|
||||
class TYPEOF(OP_CODE):
|
||||
_params = ['identifier']
|
||||
|
||||
def __init__(self, identifier):
|
||||
self.identifier = identifier
|
||||
|
||||
def eval(self, ctx):
|
||||
# typeof something_undefined does not throw reference error
|
||||
val = ctx.get(self.identifier,
|
||||
False) # <= this makes it slightly different!
|
||||
ctx.stack.append(typeof_uop(val))
|
||||
|
||||
|
||||
class POSTFIX(OP_CODE):
|
||||
_params = ['cb', 'ca', 'identifier']
|
||||
|
||||
def __init__(self, post, incr, identifier):
|
||||
self.identifier = identifier
|
||||
self.cb = 1 if incr else -1
|
||||
self.ca = -self.cb if post else 0
|
||||
|
||||
def eval(self, ctx):
|
||||
target = to_number(ctx.get(self.identifier)) + self.cb
|
||||
ctx.put(self.identifier, target)
|
||||
ctx.stack.append(target + self.ca)
|
||||
|
||||
|
||||
class POSTFIX_MEMBER(OP_CODE):
|
||||
_params = ['cb', 'ca']
|
||||
|
||||
def __init__(self, post, incr):
|
||||
self.cb = 1 if incr else -1
|
||||
self.ca = -self.cb if post else 0
|
||||
|
||||
def eval(self, ctx):
|
||||
name = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
target = to_number(get_member(left, name, ctx.space)) + self.cb
|
||||
if type(left) not in PRIMITIVES:
|
||||
left.put_member(name, target)
|
||||
|
||||
ctx.stack.append(target + self.ca)
|
||||
|
||||
|
||||
class POSTFIX_MEMBER_DOT(OP_CODE):
|
||||
_params = ['cb', 'ca', 'prop']
|
||||
|
||||
def __init__(self, post, incr, prop):
|
||||
self.cb = 1 if incr else -1
|
||||
self.ca = -self.cb if post else 0
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
left = ctx.stack.pop()
|
||||
|
||||
target = to_number(get_member_dot(left, self.prop,
|
||||
ctx.space)) + self.cb
|
||||
if type(left) not in PRIMITIVES:
|
||||
left.put(self.prop, target)
|
||||
|
||||
ctx.stack.append(target + self.ca)
|
||||
|
||||
|
||||
class DELETE(OP_CODE):
|
||||
_params = ['name']
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(ctx.delete(self.name))
|
||||
|
||||
|
||||
class DELETE_MEMBER(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
prop = to_string(ctx.stack.pop())
|
||||
obj = to_object(ctx.stack.pop(), ctx)
|
||||
ctx.stack.append(obj.delete(prop, False))
|
||||
|
||||
|
||||
# --------------------- BITWISE ----------------------
|
||||
|
||||
|
||||
class BINARY_OP(OP_CODE):
|
||||
_params = ['operator']
|
||||
|
||||
def __init__(self, operator):
|
||||
self.operator = operator
|
||||
|
||||
def eval(self, ctx):
|
||||
right = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.operator](left, right))
|
||||
|
||||
|
||||
# &&, || and conditional are implemented in bytecode
|
||||
|
||||
# --------------------- JUMPS ----------------------
|
||||
|
||||
|
||||
# simple label that will be removed from code after compilation. labels ID will be translated
|
||||
# to source code position.
|
||||
class LABEL(OP_CODE):
|
||||
_params = ['num']
|
||||
|
||||
def __init__(self, num):
|
||||
self.num = num
|
||||
|
||||
|
||||
# I implemented interpreter in the way that when an integer is returned by eval operation the execution will jump
|
||||
# to the location of the label (it is loc = label_locations[label])
|
||||
|
||||
|
||||
class BASE_JUMP(OP_CODE):
|
||||
_params = ['label']
|
||||
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
|
||||
|
||||
class JUMP(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_TRUE(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack.pop()
|
||||
if to_boolean(val):
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_EQ(BASE_JUMP):
|
||||
# this one is used in switch statement - compares last 2 values using === operator and jumps popping both if true else pops last.
|
||||
def eval(self, ctx):
|
||||
cmp = ctx.stack.pop()
|
||||
if strict_equality_op(ctx.stack[-1], cmp):
|
||||
ctx.stack.pop()
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_TRUE_WITHOUT_POP(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack[-1]
|
||||
if to_boolean(val):
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_FALSE(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack.pop()
|
||||
if not to_boolean(val):
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_FALSE_WITHOUT_POP(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack[-1]
|
||||
if not to_boolean(val):
|
||||
return self.label
|
||||
|
||||
|
||||
class POP(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
# todo remove this check later
|
||||
assert len(ctx.stack), 'Popped from empty stack!'
|
||||
del ctx.stack[-1]
|
||||
|
||||
|
||||
# class REDUCE(OP_CODE):
|
||||
# def eval(self, ctx):
|
||||
# assert len(ctx.stack)==2
|
||||
# ctx.stack[0] = ctx.stack[1]
|
||||
# del ctx.stack[1]
|
||||
|
||||
# --------------- LOADING --------------
|
||||
|
||||
|
||||
class LOAD_NONE(OP_CODE): # be careful with this :)
|
||||
_params = []
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(None)
|
||||
|
||||
|
||||
class LOAD_N_TUPLE(
|
||||
OP_CODE
|
||||
): # loads the tuple composed of n last elements on stack. elements are popped.
|
||||
_params = ['n']
|
||||
|
||||
def __init__(self, n):
|
||||
self.n = n
|
||||
|
||||
def eval(self, ctx):
|
||||
tup = tuple(ctx.stack[-self.n:])
|
||||
del ctx.stack[-self.n:]
|
||||
ctx.stack.append(tup)
|
||||
|
||||
|
||||
class LOAD_UNDEFINED(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(undefined)
|
||||
|
||||
|
||||
class LOAD_NULL(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(null)
|
||||
|
||||
|
||||
class LOAD_BOOLEAN(OP_CODE):
|
||||
_params = ['val']
|
||||
|
||||
def __init__(self, val):
|
||||
assert val in (0, 1)
|
||||
self.val = bool(val)
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(self.val)
|
||||
|
||||
|
||||
class LOAD_STRING(OP_CODE):
|
||||
_params = ['val']
|
||||
|
||||
def __init__(self, val):
|
||||
assert isinstance(val, basestring)
|
||||
self.val = unicode(val)
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(self.val)
|
||||
|
||||
|
||||
class LOAD_NUMBER(OP_CODE):
|
||||
_params = ['val']
|
||||
|
||||
def __init__(self, val):
|
||||
assert isinstance(val, (float, int, long))
|
||||
self.val = float(val)
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(self.val)
|
||||
|
||||
|
||||
class LOAD_REGEXP(OP_CODE):
|
||||
_params = ['body', 'flags']
|
||||
|
||||
def __init__(self, body, flags):
|
||||
self.body = body
|
||||
self.flags = flags
|
||||
|
||||
def eval(self, ctx):
|
||||
# we have to generate a new regexp - they are mutable
|
||||
ctx.stack.append(ctx.space.NewRegExp(self.body, self.flags))
|
||||
|
||||
|
||||
class LOAD_FUNCTION(OP_CODE):
|
||||
_params = ['start', 'params', 'name', 'is_declaration', 'definitions']
|
||||
|
||||
def __init__(self, start, params, name, is_declaration, definitions):
|
||||
assert type(start) == int
|
||||
self.start = start # its an ID of label pointing to the beginning of the function bytecode
|
||||
self.params = params
|
||||
self.name = name
|
||||
self.is_declaration = bool(is_declaration)
|
||||
self.definitions = tuple(set(definitions + params))
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(
|
||||
ctx.space.NewFunction(self.start, ctx, self.params, self.name,
|
||||
self.is_declaration, self.definitions))
|
||||
|
||||
|
||||
class LOAD_OBJECT(OP_CODE):
|
||||
_params = [
|
||||
'props'
|
||||
] # props are py string pairs (prop_name, kind): kind can be either i, g or s. (init, get, set)
|
||||
|
||||
def __init__(self, props):
|
||||
self.num = len(props)
|
||||
self.props = props
|
||||
|
||||
def eval(self, ctx):
|
||||
obj = ctx.space.NewObject()
|
||||
if self.num:
|
||||
obj._init(self.props, ctx.stack[-self.num:])
|
||||
del ctx.stack[-self.num:]
|
||||
|
||||
ctx.stack.append(obj)
|
||||
|
||||
|
||||
class LOAD_ARRAY(OP_CODE):
|
||||
_params = ['num']
|
||||
|
||||
def __init__(self, num):
|
||||
self.num = num
|
||||
|
||||
def eval(self, ctx):
|
||||
arr = ctx.space.NewArray(self.num)
|
||||
if self.num:
|
||||
arr._init(ctx.stack[-self.num:])
|
||||
del ctx.stack[-self.num:]
|
||||
ctx.stack.append(arr)
|
||||
|
||||
|
||||
class LOAD_THIS(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(ctx.THIS_BINDING)
|
||||
|
||||
|
||||
class LOAD(OP_CODE): # todo check!
|
||||
_params = ['identifier']
|
||||
|
||||
def __init__(self, identifier):
|
||||
self.identifier = identifier
|
||||
|
||||
# 11.1.2
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(ctx.get(self.identifier, throw=True))
|
||||
|
||||
|
||||
class LOAD_MEMBER(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
prop = ctx.stack.pop()
|
||||
obj = ctx.stack.pop()
|
||||
ctx.stack.append(get_member(obj, prop, ctx.space))
|
||||
|
||||
|
||||
class LOAD_MEMBER_DOT(OP_CODE):
|
||||
_params = ['prop']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
obj = ctx.stack.pop()
|
||||
ctx.stack.append(get_member_dot(obj, self.prop, ctx.space))
|
||||
|
||||
|
||||
# --------------- STORING --------------
|
||||
|
||||
|
||||
class STORE(OP_CODE):
|
||||
_params = ['identifier']
|
||||
|
||||
def __init__(self, identifier):
|
||||
self.identifier = identifier
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack[-1] # don't pop
|
||||
ctx.put(self.identifier, value)
|
||||
|
||||
|
||||
class STORE_MEMBER(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
name = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
typ = type(left)
|
||||
if typ in PRIMITIVES:
|
||||
prop = to_string(name)
|
||||
if typ == NULL_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot set property '%s' of null" % prop)
|
||||
elif typ == UNDEFINED_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot set property '%s' of undefined" % prop)
|
||||
# just ignore...
|
||||
else:
|
||||
left.put_member(name, value)
|
||||
|
||||
ctx.stack.append(value)
|
||||
|
||||
|
||||
class STORE_MEMBER_DOT(OP_CODE):
|
||||
_params = ['prop']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
typ = type(left)
|
||||
if typ in PRIMITIVES:
|
||||
if typ == NULL_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot set property '%s' of null" % self.prop)
|
||||
elif typ == UNDEFINED_TYPE:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"Cannot set property '%s' of undefined" % self.prop)
|
||||
# just ignore...
|
||||
else:
|
||||
left.put(self.prop, value)
|
||||
ctx.stack.append(value)
|
||||
|
||||
|
||||
class STORE_OP(OP_CODE):
|
||||
_params = ['identifier', 'op']
|
||||
|
||||
def __init__(self, identifier, op):
|
||||
self.identifier = identifier
|
||||
self.op = op
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
new_value = BINARY_OPERATIONS[self.op](ctx.get(self.identifier), value)
|
||||
ctx.put(self.identifier, new_value)
|
||||
ctx.stack.append(new_value)
|
||||
|
||||
|
||||
class STORE_MEMBER_OP(OP_CODE):
|
||||
_params = ['op']
|
||||
|
||||
def __init__(self, op):
|
||||
self.op = op
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
name = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
typ = type(left)
|
||||
if typ in PRIMITIVES:
|
||||
if typ is NULL_TYPE:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"Cannot set property '%s' of null" % to_string(name))
|
||||
elif typ is UNDEFINED_TYPE:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"Cannot set property '%s' of undefined" % to_string(name))
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(
|
||||
left, name, ctx.space), value))
|
||||
return
|
||||
else:
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(
|
||||
left, name, ctx.space), value))
|
||||
left.put_member(name, ctx.stack[-1])
|
||||
|
||||
|
||||
class STORE_MEMBER_DOT_OP(OP_CODE):
|
||||
_params = ['prop', 'op']
|
||||
|
||||
def __init__(self, prop, op):
|
||||
self.prop = prop
|
||||
self.op = op
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
typ = type(left)
|
||||
if typ in PRIMITIVES:
|
||||
if typ == NULL_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot set property '%s' of null" % self.prop)
|
||||
elif typ == UNDEFINED_TYPE:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"Cannot set property '%s' of undefined" % self.prop)
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(
|
||||
left, self.prop, ctx.space), value))
|
||||
return
|
||||
else:
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(
|
||||
left, self.prop, ctx.space), value))
|
||||
left.put(self.prop, ctx.stack[-1])
|
||||
|
||||
|
||||
# --------------- CALLS --------------
|
||||
|
||||
|
||||
def bytecode_call(ctx, func, this, args):
|
||||
if type(func) is not PyJsFunction:
|
||||
raise MakeError('TypeError', "%s is not a function" % Type(func))
|
||||
if func.is_native: # call to built-in function or method
|
||||
ctx.stack.append(func.call(this, args))
|
||||
return None
|
||||
|
||||
# therefore not native. we have to return (new_context, function_label) to instruct interpreter to call
|
||||
return func._generate_my_context(this, args), func.code
|
||||
|
||||
|
||||
class CALL(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
args = ctx.stack.pop()
|
||||
func = ctx.stack.pop()
|
||||
|
||||
return bytecode_call(ctx, func, ctx.space.GlobalObj, args)
|
||||
|
||||
|
||||
class CALL_METHOD(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
args = ctx.stack.pop()
|
||||
prop = ctx.stack.pop()
|
||||
base = ctx.stack.pop()
|
||||
|
||||
func = get_member(base, prop, ctx.space)
|
||||
|
||||
return bytecode_call(ctx, func, base, args)
|
||||
|
||||
|
||||
class CALL_METHOD_DOT(OP_CODE):
|
||||
_params = ['prop']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
args = ctx.stack.pop()
|
||||
base = ctx.stack.pop()
|
||||
|
||||
func = get_member_dot(base, self.prop, ctx.space)
|
||||
|
||||
return bytecode_call(ctx, func, base, args)
|
||||
|
||||
|
||||
class CALL_NO_ARGS(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
func = ctx.stack.pop()
|
||||
|
||||
return bytecode_call(ctx, func, ctx.space.GlobalObj, ())
|
||||
|
||||
|
||||
class CALL_METHOD_NO_ARGS(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
prop = ctx.stack.pop()
|
||||
base = ctx.stack.pop()
|
||||
|
||||
func = get_member(base, prop, ctx.space)
|
||||
|
||||
return bytecode_call(ctx, func, base, ())
|
||||
|
||||
|
||||
class CALL_METHOD_DOT_NO_ARGS(OP_CODE):
|
||||
_params = ['prop']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
base = ctx.stack.pop()
|
||||
|
||||
func = get_member_dot(base, self.prop, ctx.space)
|
||||
|
||||
return bytecode_call(ctx, func, base, ())
|
||||
|
||||
|
||||
class NOP(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
pass
|
||||
|
||||
|
||||
class RETURN(OP_CODE):
|
||||
def eval(
|
||||
self, ctx
|
||||
): # remember to load the return value on stack before using RETURN op.
|
||||
return (None, None)
|
||||
|
||||
|
||||
class NEW(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
args = ctx.stack.pop()
|
||||
constructor = ctx.stack.pop()
|
||||
if type(constructor) in PRIMITIVES or not hasattr(
|
||||
constructor, 'create'):
|
||||
raise MakeError('TypeError',
|
||||
'%s is not a constructor' % Type(constructor))
|
||||
ctx.stack.append(constructor.create(args, space=ctx.space))
|
||||
|
||||
|
||||
class NEW_NO_ARGS(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
constructor = ctx.stack.pop()
|
||||
if type(constructor) in PRIMITIVES or not hasattr(
|
||||
constructor, 'create'):
|
||||
raise MakeError('TypeError',
|
||||
'%s is not a constructor' % Type(constructor))
|
||||
ctx.stack.append(constructor.create((), space=ctx.space))
|
||||
|
||||
|
||||
# --------------- EXCEPTIONS --------------
|
||||
|
||||
|
||||
class THROW(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
raise MakeError(None, None, ctx.stack.pop())
|
||||
|
||||
|
||||
class TRY_CATCH_FINALLY(OP_CODE):
|
||||
_params = [
|
||||
'try_label', 'catch_label', 'catch_variable', 'finally_label',
|
||||
'finally_present', 'end_label'
|
||||
]
|
||||
|
||||
def __init__(self, try_label, catch_label, catch_variable, finally_label,
|
||||
finally_present, end_label):
|
||||
self.try_label = try_label
|
||||
self.catch_label = catch_label
|
||||
self.catch_variable = catch_variable
|
||||
self.finally_label = finally_label
|
||||
self.finally_present = finally_present
|
||||
self.end_label = end_label
|
||||
|
||||
def eval(self, ctx):
|
||||
# 4 different exectution results
|
||||
# 0=normal, 1=return, 2=jump_outside, 3=errors
|
||||
# execute_fragment_under_context returns:
|
||||
# (return_value, typ, jump_loc/error)
|
||||
|
||||
ctx.stack.pop()
|
||||
|
||||
# execute try statement
|
||||
try_status = ctx.space.exe.execute_fragment_under_context(
|
||||
ctx, self.try_label, self.catch_label)
|
||||
|
||||
errors = try_status[1] == 3
|
||||
|
||||
# catch
|
||||
if errors and self.catch_variable is not None:
|
||||
# generate catch block context...
|
||||
catch_context = Scope({
|
||||
self.catch_variable:
|
||||
try_status[2].get_thrown_value(ctx.space)
|
||||
}, ctx.space, ctx)
|
||||
catch_context.THIS_BINDING = ctx.THIS_BINDING
|
||||
catch_status = ctx.space.exe.execute_fragment_under_context(
|
||||
catch_context, self.catch_label, self.finally_label)
|
||||
else:
|
||||
catch_status = None
|
||||
|
||||
# finally
|
||||
if self.finally_present:
|
||||
finally_status = ctx.space.exe.execute_fragment_under_context(
|
||||
ctx, self.finally_label, self.end_label)
|
||||
else:
|
||||
finally_status = None
|
||||
|
||||
# now return controls
|
||||
other_status = catch_status or try_status
|
||||
if finally_status is None or (finally_status[1] == 0
|
||||
and other_status[1] != 0):
|
||||
winning_status = other_status
|
||||
else:
|
||||
winning_status = finally_status
|
||||
|
||||
val, typ, spec = winning_status
|
||||
if typ == 0: # normal
|
||||
ctx.stack.append(val)
|
||||
return
|
||||
elif typ == 1: # return
|
||||
ctx.stack.append(spec)
|
||||
return None, None # send return signal
|
||||
elif typ == 2: # jump outside
|
||||
ctx.stack.append(val)
|
||||
return spec
|
||||
elif typ == 3:
|
||||
# throw is made with empty stack as usual
|
||||
raise spec
|
||||
else:
|
||||
raise RuntimeError('Invalid return code')
|
||||
|
||||
|
||||
# ------------ WITH + ITERATORS ----------
|
||||
|
||||
|
||||
class WITH(OP_CODE):
|
||||
_params = ['beg_label', 'end_label']
|
||||
|
||||
def __init__(self, beg_label, end_label):
|
||||
self.beg_label = beg_label
|
||||
self.end_label = end_label
|
||||
|
||||
def eval(self, ctx):
|
||||
obj = to_object(ctx.stack.pop(), ctx.space)
|
||||
|
||||
with_context = Scope(
|
||||
obj, ctx.space, ctx) # todo actually use the obj to modify the ctx
|
||||
with_context.THIS_BINDING = ctx.THIS_BINDING
|
||||
status = ctx.space.exe.execute_fragment_under_context(
|
||||
with_context, self.beg_label, self.end_label)
|
||||
|
||||
val, typ, spec = status
|
||||
|
||||
if typ != 3: # exception
|
||||
ctx.stack.pop()
|
||||
|
||||
if typ == 0: # normal
|
||||
ctx.stack.append(val)
|
||||
return
|
||||
elif typ == 1: # return
|
||||
ctx.stack.append(spec)
|
||||
return None, None # send return signal
|
||||
elif typ == 2: # jump outside
|
||||
ctx.stack.append(val)
|
||||
return spec
|
||||
elif typ == 3: # exception
|
||||
# throw is made with empty stack as usual
|
||||
raise spec
|
||||
else:
|
||||
raise RuntimeError('Invalid return code')
|
||||
|
||||
|
||||
class FOR_IN(OP_CODE):
|
||||
_params = ['name', 'body_start_label', 'continue_label', 'break_label']
|
||||
|
||||
def __init__(self, name, body_start_label, continue_label, break_label):
|
||||
self.name = name
|
||||
self.body_start_label = body_start_label
|
||||
self.continue_label = continue_label
|
||||
self.break_label = break_label
|
||||
|
||||
def eval(self, ctx):
|
||||
iterable = ctx.stack.pop()
|
||||
if is_null(iterable) or is_undefined(iterable):
|
||||
ctx.stack.pop()
|
||||
ctx.stack.append(undefined)
|
||||
return self.break_label
|
||||
|
||||
obj = to_object(iterable, ctx.space)
|
||||
|
||||
for e in sorted(obj.own):
|
||||
if not obj.own[e]['enumerable']:
|
||||
continue
|
||||
|
||||
ctx.put(
|
||||
self.name, e
|
||||
) # JS would have been so much nicer if this was ctx.space.put(self.name, obj.get(e))
|
||||
|
||||
# evaluate the body
|
||||
status = ctx.space.exe.execute_fragment_under_context(
|
||||
ctx, self.body_start_label, self.break_label)
|
||||
|
||||
val, typ, spec = status
|
||||
|
||||
if typ != 3: # exception
|
||||
ctx.stack.pop()
|
||||
|
||||
if typ == 0: # normal
|
||||
ctx.stack.append(val)
|
||||
continue
|
||||
elif typ == 1: # return
|
||||
ctx.stack.append(spec)
|
||||
return None, None # send return signal
|
||||
elif typ == 2: # jump outside
|
||||
# now have to figure out whether this is a continue or something else...
|
||||
ctx.stack.append(val)
|
||||
if spec == self.continue_label:
|
||||
# just a continue, perform next iteration as normal
|
||||
continue
|
||||
return spec # break or smth, go there and finish the iteration
|
||||
elif typ == 3: # exception
|
||||
# throw is made with empty stack as usual
|
||||
raise spec
|
||||
else:
|
||||
raise RuntimeError('Invalid return code')
|
||||
|
||||
return self.break_label
|
||||
|
||||
|
||||
# all opcodes...
|
||||
OP_CODES = {}
|
||||
g = ''
|
||||
for g in globals():
|
||||
try:
|
||||
if not issubclass(globals()[g], OP_CODE) or g is 'OP_CODE':
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
OP_CODES[g] = globals()[g]
|
||||
@@ -1,314 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from simplex import *
|
||||
from conversions import *
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Unary operations
|
||||
|
||||
|
||||
# -x
|
||||
def minus_uop(self):
|
||||
return -to_number(self)
|
||||
|
||||
|
||||
# +x
|
||||
def plus_uop(self): # +u
|
||||
return to_number(self)
|
||||
|
||||
|
||||
# !x
|
||||
def logical_negation_uop(self): # !u cant do 'not u' :(
|
||||
return not to_boolean(self)
|
||||
|
||||
|
||||
# typeof x
|
||||
def typeof_uop(self):
|
||||
if is_callable(self):
|
||||
return u'function'
|
||||
typ = Type(self).lower()
|
||||
if typ == u'null':
|
||||
typ = u'object' # absolutely idiotic...
|
||||
return typ
|
||||
|
||||
|
||||
# ~u
|
||||
def bit_invert_uop(self):
|
||||
return float(to_int32(float(~to_int32(self))))
|
||||
|
||||
|
||||
# void
|
||||
def void_op(self):
|
||||
return undefined
|
||||
|
||||
|
||||
UNARY_OPERATIONS = {
|
||||
'+': plus_uop,
|
||||
'-': minus_uop,
|
||||
'!': logical_negation_uop,
|
||||
'~': bit_invert_uop,
|
||||
'void': void_op,
|
||||
'typeof':
|
||||
typeof_uop, # this one only for member expressions! for identifiers its slightly different...
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ----- binary ops -------
|
||||
|
||||
# Bitwise operators
|
||||
# <<, >>, &, ^, |, ~
|
||||
|
||||
|
||||
# <<
|
||||
def bit_lshift_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_uint32(other)
|
||||
shiftCount = rnum & 0x1F
|
||||
return float(to_int32(float(lnum << shiftCount)))
|
||||
|
||||
|
||||
# >>
|
||||
def bit_rshift_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_uint32(other)
|
||||
shiftCount = rnum & 0x1F
|
||||
return float(to_int32(float(lnum >> shiftCount)))
|
||||
|
||||
|
||||
# >>>
|
||||
def bit_bshift_op(self, other):
|
||||
lnum = to_uint32(self)
|
||||
rnum = to_uint32(other)
|
||||
shiftCount = rnum & 0x1F
|
||||
return float(to_uint32(float(lnum >> shiftCount)))
|
||||
|
||||
|
||||
# &
|
||||
def bit_and_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_int32(other)
|
||||
return float(to_int32(float(lnum & rnum)))
|
||||
|
||||
|
||||
# ^
|
||||
def bit_xor_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_int32(other)
|
||||
return float(to_int32(float(lnum ^ rnum)))
|
||||
|
||||
|
||||
# |
|
||||
def bit_or_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_int32(other)
|
||||
return float(to_int32(float(lnum | rnum)))
|
||||
|
||||
|
||||
# Additive operators
|
||||
# + and - are implemented here
|
||||
|
||||
|
||||
# +
|
||||
def add_op(self, other):
|
||||
if type(self) is float and type(other) is float:
|
||||
return self + other
|
||||
if type(self) is unicode and type(other) is unicode:
|
||||
return self + other
|
||||
# standard way...
|
||||
a = to_primitive(self)
|
||||
b = to_primitive(other)
|
||||
if type(a) is unicode or type(b) is unicode: # string wins hehe
|
||||
return to_string(a) + to_string(b)
|
||||
return to_number(a) + to_number(b)
|
||||
|
||||
|
||||
# -
|
||||
def sub_op(self, other):
|
||||
return to_number(self) - to_number(other)
|
||||
|
||||
|
||||
# Multiplicative operators
|
||||
# *, / and % are implemented here
|
||||
|
||||
|
||||
# *
|
||||
def mul_op(self, other):
|
||||
return to_number(self) * to_number(other)
|
||||
|
||||
|
||||
# /
|
||||
def div_op(self, other):
|
||||
a = to_number(self)
|
||||
b = to_number(other)
|
||||
if b:
|
||||
return a / float(b) # ensure at least one is a float.
|
||||
if not a or a != a:
|
||||
return NaN
|
||||
return Infinity if a > 0 else -Infinity
|
||||
|
||||
|
||||
# %
|
||||
def mod_op(self, other):
|
||||
a = to_number(self)
|
||||
b = to_number(other)
|
||||
if abs(a) == Infinity or not b:
|
||||
return NaN
|
||||
if abs(b) == Infinity:
|
||||
return a
|
||||
pyres = a % b # different signs in python and javascript
|
||||
# python has the same sign as b and js has the same
|
||||
# sign as a.
|
||||
if a < 0 and pyres > 0:
|
||||
pyres -= abs(b)
|
||||
elif a > 0 and pyres < 0:
|
||||
pyres += abs(b)
|
||||
return float(pyres)
|
||||
|
||||
|
||||
# Comparisons
|
||||
# <, <=, !=, ==, >=, > are implemented here.
|
||||
def abstract_relational_comparison(self, other,
|
||||
self_first=True): # todo speed up!
|
||||
''' self<other if self_first else other<self.
|
||||
Returns the result of the question: is self smaller than other?
|
||||
in case self_first is false it returns the answer of:
|
||||
is other smaller than self.
|
||||
result is PyJs type: bool or undefined'''
|
||||
|
||||
px = to_primitive(self, 'Number')
|
||||
py = to_primitive(other, 'Number')
|
||||
if not self_first: # reverse order
|
||||
px, py = py, px
|
||||
if not (Type(px) == 'String' and Type(py) == 'String'):
|
||||
px, py = to_number(px), to_number(py)
|
||||
if is_nan(px) or is_nan(py):
|
||||
return None # watch out here!
|
||||
return px < py # same cmp algorithm
|
||||
else:
|
||||
# I am pretty sure that python has the same
|
||||
# string cmp algorithm but I have to confirm it
|
||||
return px < py
|
||||
|
||||
|
||||
# <
|
||||
def less_op(self, other):
|
||||
res = abstract_relational_comparison(self, other, True)
|
||||
if res is None:
|
||||
return False
|
||||
return res
|
||||
|
||||
|
||||
# <=
|
||||
def less_eq_op(self, other):
|
||||
res = abstract_relational_comparison(self, other, False)
|
||||
if res is None:
|
||||
return False
|
||||
return not res
|
||||
|
||||
|
||||
# >=
|
||||
def greater_eq_op(self, other):
|
||||
res = abstract_relational_comparison(self, other, True)
|
||||
if res is None:
|
||||
return False
|
||||
return not res
|
||||
|
||||
|
||||
# >
|
||||
def greater_op(self, other):
|
||||
res = abstract_relational_comparison(self, other, False)
|
||||
if res is None:
|
||||
return False
|
||||
return res
|
||||
|
||||
|
||||
# equality
|
||||
|
||||
|
||||
def abstract_equality_op(self, other):
|
||||
''' returns the result of JS == compare.
|
||||
result is PyJs type: bool'''
|
||||
tx, ty = Type(self), Type(other)
|
||||
if tx == ty:
|
||||
if tx == 'Undefined' or tx == 'Null':
|
||||
return True
|
||||
if tx == 'Number' or tx == 'String' or tx == 'Boolean':
|
||||
return self == other
|
||||
return self is other # Object
|
||||
elif (tx == 'Undefined' and ty == 'Null') or (ty == 'Undefined'
|
||||
and tx == 'Null'):
|
||||
return True
|
||||
elif tx == 'Number' and ty == 'String':
|
||||
return abstract_equality_op(self, to_number(other))
|
||||
elif tx == 'String' and ty == 'Number':
|
||||
return abstract_equality_op(to_number(self), other)
|
||||
elif tx == 'Boolean':
|
||||
return abstract_equality_op(to_number(self), other)
|
||||
elif ty == 'Boolean':
|
||||
return abstract_equality_op(self, to_number(other))
|
||||
elif (tx == 'String' or tx == 'Number') and is_object(other):
|
||||
return abstract_equality_op(self, to_primitive(other))
|
||||
elif (ty == 'String' or ty == 'Number') and is_object(self):
|
||||
return abstract_equality_op(to_primitive(self), other)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def abstract_inequality_op(self, other):
|
||||
return not abstract_equality_op(self, other)
|
||||
|
||||
|
||||
def strict_equality_op(self, other):
|
||||
typ = Type(self)
|
||||
if typ != Type(other):
|
||||
return False
|
||||
if typ == 'Undefined' or typ == 'Null':
|
||||
return True
|
||||
if typ == 'Boolean' or typ == 'String' or typ == 'Number':
|
||||
return self == other
|
||||
else: # object
|
||||
return self is other # Id compare.
|
||||
|
||||
|
||||
def strict_inequality_op(self, other):
|
||||
return not strict_equality_op(self, other)
|
||||
|
||||
|
||||
def instanceof_op(self, other):
|
||||
'''checks if self is instance of other'''
|
||||
if not hasattr(other, 'has_instance'):
|
||||
return False
|
||||
return other.has_instance(self)
|
||||
|
||||
|
||||
def in_op(self, other):
|
||||
'''checks if self is in other'''
|
||||
if not is_object(other):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"You can\'t use 'in' operator to search in non-objects")
|
||||
return other.has_property(to_string(self))
|
||||
|
||||
|
||||
BINARY_OPERATIONS = {
|
||||
'+': add_op,
|
||||
'-': sub_op,
|
||||
'*': mul_op,
|
||||
'/': div_op,
|
||||
'%': mod_op,
|
||||
'<<': bit_lshift_op,
|
||||
'>>': bit_rshift_op,
|
||||
'>>>': bit_bshift_op,
|
||||
'|': bit_or_op,
|
||||
'&': bit_and_op,
|
||||
'^': bit_xor_op,
|
||||
'==': abstract_equality_op,
|
||||
'!=': abstract_inequality_op,
|
||||
'===': strict_equality_op,
|
||||
'!==': strict_inequality_op,
|
||||
'<': less_op,
|
||||
'<=': less_eq_op,
|
||||
'>': greater_op,
|
||||
'>=': greater_eq_op,
|
||||
'in': in_op,
|
||||
'instanceof': instanceof_op,
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
@@ -1,489 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from ..operations import strict_equality_op
|
||||
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
xrange = range
|
||||
import functools
|
||||
|
||||
ARR_STACK = set({})
|
||||
|
||||
|
||||
class ArrayPrototype:
|
||||
def toString(this, args):
|
||||
arr = to_object(this, args.space)
|
||||
func = arr.get('join')
|
||||
if not is_callable(func):
|
||||
return u'[object %s]' % GetClass(arr)
|
||||
return func.call(this, ())
|
||||
|
||||
def toLocaleString(this, args):
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
# separator is simply a comma ','
|
||||
if not arr_len:
|
||||
return ''
|
||||
res = []
|
||||
for i in xrange(arr_len):
|
||||
element = array.get(unicode(i))
|
||||
if is_undefined(element) or is_null(element):
|
||||
res.append('')
|
||||
else:
|
||||
cand = to_object(element, args.space)
|
||||
str_func = cand.get('toLocaleString')
|
||||
if not is_callable(str_func):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'toLocaleString method of item at index %d is not callable'
|
||||
% i)
|
||||
res.append(to_string(str_func.call(cand, ())))
|
||||
return ','.join(res)
|
||||
|
||||
def concat(this, args):
|
||||
array = to_object(this, args.space)
|
||||
items = [array]
|
||||
items.extend(tuple(args))
|
||||
A = []
|
||||
for E in items:
|
||||
if GetClass(E) == 'Array':
|
||||
k = 0
|
||||
e_len = js_arr_length(E)
|
||||
while k < e_len:
|
||||
if E.has_property(unicode(k)):
|
||||
A.append(E.get(unicode(k)))
|
||||
k += 1
|
||||
else:
|
||||
A.append(E)
|
||||
return args.space.ConstructArray(A)
|
||||
|
||||
def join(this, args):
|
||||
ARR_STACK.add(this)
|
||||
array = to_object(this, args.space)
|
||||
separator = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
separator = ',' if is_undefined(separator) else to_string(separator)
|
||||
elems = []
|
||||
for e in xrange(arr_len):
|
||||
elem = array.get(unicode(e))
|
||||
if elem in ARR_STACK:
|
||||
s = ''
|
||||
else:
|
||||
s = to_string(elem)
|
||||
elems.append(
|
||||
s if not (is_undefined(elem) or is_null(elem)) else '')
|
||||
res = separator.join(elems)
|
||||
ARR_STACK.remove(this)
|
||||
return res
|
||||
|
||||
def pop(this, args): #todo check
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
if not arr_len:
|
||||
array.put('length', float(arr_len))
|
||||
return undefined
|
||||
ind = unicode(arr_len - 1)
|
||||
element = array.get(ind)
|
||||
array.delete(ind)
|
||||
array.put('length', float(arr_len - 1))
|
||||
return element
|
||||
|
||||
def push(this, args):
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
to_put = tuple(args)
|
||||
i = arr_len
|
||||
for i, e in enumerate(to_put, arr_len):
|
||||
array.put(unicode(i), e, True)
|
||||
array.put('length', float(arr_len + len(to_put)), True)
|
||||
return float(i)
|
||||
|
||||
def reverse(this, args):
|
||||
array = to_object(this, args.space)
|
||||
vals = js_array_to_list(array)
|
||||
has_props = [
|
||||
array.has_property(unicode(e))
|
||||
for e in xrange(js_arr_length(array))
|
||||
]
|
||||
vals.reverse()
|
||||
has_props.reverse()
|
||||
for i, val in enumerate(vals):
|
||||
if has_props[i]:
|
||||
array.put(unicode(i), val)
|
||||
else:
|
||||
array.delete(unicode(i))
|
||||
return array
|
||||
|
||||
def shift(this, args):
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
if not arr_len:
|
||||
array.put('length', 0.)
|
||||
return undefined
|
||||
first = array.get('0')
|
||||
for k in xrange(1, arr_len):
|
||||
from_s, to_s = unicode(k), unicode(k - 1)
|
||||
if array.has_property(from_s):
|
||||
array.put(to_s, array.get(from_s))
|
||||
else:
|
||||
array.delete(to_s)
|
||||
array.delete(unicode(arr_len - 1))
|
||||
array.put('length', float(arr_len - 1))
|
||||
return first
|
||||
|
||||
def slice(this, args): # todo check
|
||||
array = to_object(this, args.space)
|
||||
start = get_arg(args, 0)
|
||||
end = get_arg(args, 1)
|
||||
arr_len = js_arr_length(array)
|
||||
relative_start = to_int(start)
|
||||
k = max((arr_len + relative_start), 0) if relative_start < 0 else min(
|
||||
relative_start, arr_len)
|
||||
relative_end = arr_len if is_undefined(end) else to_int(end)
|
||||
final = max((arr_len + relative_end), 0) if relative_end < 0 else min(
|
||||
relative_end, arr_len)
|
||||
res = []
|
||||
n = 0
|
||||
while k < final:
|
||||
pk = unicode(k)
|
||||
if array.has_property(pk):
|
||||
res.append(array.get(pk))
|
||||
k += 1
|
||||
n += 1
|
||||
return args.space.ConstructArray(res)
|
||||
|
||||
def sort(
|
||||
this, args
|
||||
): # todo: this assumes array continous (not sparse) - fix for sparse arrays
|
||||
cmpfn = get_arg(args, 0)
|
||||
if not GetClass(this) in ('Array', 'Arguments'):
|
||||
return to_object(this, args.space) # do nothing
|
||||
arr_len = js_arr_length(this)
|
||||
if not arr_len:
|
||||
return this
|
||||
arr = [
|
||||
(this.get(unicode(e)) if this.has_property(unicode(e)) else None)
|
||||
for e in xrange(arr_len)
|
||||
]
|
||||
if not is_callable(cmpfn):
|
||||
cmpfn = None
|
||||
cmp = lambda a, b: sort_compare(a, b, cmpfn)
|
||||
if six.PY3:
|
||||
key = functools.cmp_to_key(cmp)
|
||||
arr.sort(key=key)
|
||||
else:
|
||||
arr.sort(cmp=cmp)
|
||||
for i in xrange(arr_len):
|
||||
if arr[i] is None:
|
||||
this.delete(unicode(i))
|
||||
else:
|
||||
this.put(unicode(i), arr[i])
|
||||
return this
|
||||
|
||||
def splice(this, args):
|
||||
# 1-8
|
||||
array = to_object(this, args.space)
|
||||
start = get_arg(args, 0)
|
||||
deleteCount = get_arg(args, 1)
|
||||
arr_len = js_arr_length(this)
|
||||
relative_start = to_int(start)
|
||||
actual_start = max(
|
||||
(arr_len + relative_start), 0) if relative_start < 0 else min(
|
||||
relative_start, arr_len)
|
||||
actual_delete_count = min(
|
||||
max(to_int(deleteCount), 0), arr_len - actual_start)
|
||||
k = 0
|
||||
A = args.space.NewArray(0)
|
||||
# 9
|
||||
while k < actual_delete_count:
|
||||
if array.has_property(unicode(actual_start + k)):
|
||||
A.put(unicode(k), array.get(unicode(actual_start + k)))
|
||||
k += 1
|
||||
# 10-11
|
||||
items = list(args)[2:]
|
||||
items_len = len(items)
|
||||
# 12
|
||||
if items_len < actual_delete_count:
|
||||
k = actual_start
|
||||
while k < (arr_len - actual_delete_count):
|
||||
fr = unicode(k + actual_delete_count)
|
||||
to = unicode(k + items_len)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k += 1
|
||||
k = arr_len
|
||||
while k > (arr_len - actual_delete_count + items_len):
|
||||
array.delete(unicode(k - 1))
|
||||
k -= 1
|
||||
# 13
|
||||
elif items_len > actual_delete_count:
|
||||
k = arr_len - actual_delete_count
|
||||
while k > actual_start:
|
||||
fr = unicode(k + actual_delete_count - 1)
|
||||
to = unicode(k + items_len - 1)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k -= 1
|
||||
# 14-17
|
||||
k = actual_start
|
||||
while items:
|
||||
E = items.pop(0)
|
||||
array.put(unicode(k), E)
|
||||
k += 1
|
||||
array.put('length', float(arr_len - actual_delete_count + items_len))
|
||||
return A
|
||||
|
||||
def unshift(this, args):
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
argCount = len(args)
|
||||
k = arr_len
|
||||
while k > 0:
|
||||
fr = unicode(k - 1)
|
||||
to = unicode(k + argCount - 1)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k -= 1
|
||||
items = tuple(args)
|
||||
for j, e in enumerate(items):
|
||||
array.put(unicode(j), e)
|
||||
array.put('length', float(arr_len + argCount))
|
||||
return float(arr_len + argCount)
|
||||
|
||||
def indexOf(this, args):
|
||||
array = to_object(this, args.space)
|
||||
searchElement = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if arr_len == 0:
|
||||
return -1.
|
||||
if len(args) > 1:
|
||||
n = to_int(args[1])
|
||||
else:
|
||||
n = 0
|
||||
if n >= arr_len:
|
||||
return -1.
|
||||
if n >= 0:
|
||||
k = n
|
||||
else:
|
||||
k = arr_len - abs(n)
|
||||
if k < 0:
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
elementK = array.get(unicode(k))
|
||||
if strict_equality_op(searchElement, elementK):
|
||||
return float(k)
|
||||
k += 1
|
||||
return -1.
|
||||
|
||||
def lastIndexOf(this, args):
|
||||
array = to_object(this, args.space)
|
||||
searchElement = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if arr_len == 0:
|
||||
return -1.
|
||||
if len(args) > 1:
|
||||
n = to_int(args[1])
|
||||
else:
|
||||
n = arr_len - 1
|
||||
if n >= 0:
|
||||
k = min(n, arr_len - 1)
|
||||
else:
|
||||
k = arr_len - abs(n)
|
||||
while k >= 0:
|
||||
if array.has_property(unicode(k)):
|
||||
elementK = array.get(unicode(k))
|
||||
if strict_equality_op(searchElement, elementK):
|
||||
return float(k)
|
||||
k -= 1
|
||||
return -1.
|
||||
|
||||
def every(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = get_arg(args, 1)
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
if not to_boolean(
|
||||
callbackfn.call(T, (kValue, float(k), array))):
|
||||
return False
|
||||
k += 1
|
||||
return True
|
||||
|
||||
def some(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = get_arg(args, 1)
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
if to_boolean(callbackfn.call(T, (kValue, float(k), array))):
|
||||
return True
|
||||
k += 1
|
||||
return False
|
||||
|
||||
def forEach(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
_this = get_arg(args, 1)
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
sk = unicode(k)
|
||||
if array.has_property(sk):
|
||||
kValue = array.get(sk)
|
||||
callbackfn.call(_this, (kValue, float(k), array))
|
||||
k += 1
|
||||
return undefined
|
||||
|
||||
def map(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
_this = get_arg(args, 1)
|
||||
k = 0
|
||||
A = args.space.NewArray(0)
|
||||
while k < arr_len:
|
||||
Pk = unicode(k)
|
||||
if array.has_property(Pk):
|
||||
kValue = array.get(Pk)
|
||||
mappedValue = callbackfn.call(_this, (kValue, float(k), array))
|
||||
A.define_own_property(
|
||||
Pk, {
|
||||
'value': mappedValue,
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
k += 1
|
||||
return A
|
||||
|
||||
def filter(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
_this = get_arg(args, 1)
|
||||
k = 0
|
||||
res = []
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
if to_boolean(
|
||||
callbackfn.call(_this, (kValue, float(k), array))):
|
||||
res.append(kValue)
|
||||
k += 1
|
||||
return args.space.ConstructArray(res)
|
||||
|
||||
def reduce(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
if not arr_len and len(args) < 2:
|
||||
raise MakeError('TypeError',
|
||||
'Reduce of empty array with no initial value')
|
||||
k = 0
|
||||
accumulator = undefined
|
||||
if len(args) > 1: # initial value present
|
||||
accumulator = args[1]
|
||||
else:
|
||||
kPresent = False
|
||||
while not kPresent and k < arr_len:
|
||||
kPresent = array.has_property(unicode(k))
|
||||
if kPresent:
|
||||
accumulator = array.get(unicode(k))
|
||||
k += 1
|
||||
if not kPresent:
|
||||
raise MakeError('TypeError',
|
||||
'Reduce of empty array with no initial value')
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
accumulator = callbackfn.call(
|
||||
undefined, (accumulator, kValue, float(k), array))
|
||||
k += 1
|
||||
return accumulator
|
||||
|
||||
def reduceRight(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
if not arr_len and len(args) < 2:
|
||||
raise MakeError('TypeError',
|
||||
'Reduce of empty array with no initial value')
|
||||
k = arr_len - 1
|
||||
accumulator = undefined
|
||||
|
||||
if len(args) > 1: # initial value present
|
||||
accumulator = args[1]
|
||||
else:
|
||||
kPresent = False
|
||||
while not kPresent and k >= 0:
|
||||
kPresent = array.has_property(unicode(k))
|
||||
if kPresent:
|
||||
accumulator = array.get(unicode(k))
|
||||
k -= 1
|
||||
if not kPresent:
|
||||
raise MakeError('TypeError',
|
||||
'Reduce of empty array with no initial value')
|
||||
while k >= 0:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
accumulator = callbackfn.call(
|
||||
undefined, (accumulator, kValue, float(k), array))
|
||||
k -= 1
|
||||
return accumulator
|
||||
|
||||
|
||||
def sort_compare(a, b, comp):
|
||||
if a is None:
|
||||
if b is None:
|
||||
return 0
|
||||
return 1
|
||||
if b is None:
|
||||
if a is None:
|
||||
return 0
|
||||
return -1
|
||||
if is_undefined(a):
|
||||
if is_undefined(b):
|
||||
return 0
|
||||
return 1
|
||||
if is_undefined(b):
|
||||
if is_undefined(a):
|
||||
return 0
|
||||
return -1
|
||||
if comp is not None:
|
||||
res = comp.call(undefined, (a, b))
|
||||
return to_int(res)
|
||||
x, y = to_string(a), to_string(b)
|
||||
if x < y:
|
||||
return -1
|
||||
elif x > y:
|
||||
return 1
|
||||
return 0
|
||||
@@ -1,22 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
class BooleanPrototype:
|
||||
def toString(this, args):
|
||||
if GetClass(this) != 'Boolean':
|
||||
raise MakeError('TypeError',
|
||||
'Boolean.prototype.toString is not generic')
|
||||
if is_object(this):
|
||||
this = this.value
|
||||
return u'true' if this else u'false'
|
||||
|
||||
def valueOf(this, args):
|
||||
if GetClass(this) != 'Boolean':
|
||||
raise MakeError('TypeError',
|
||||
'Boolean.prototype.valueOf is not generic')
|
||||
if is_object(this):
|
||||
this = this.value
|
||||
return this
|
||||
@@ -1,15 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
class ErrorPrototype:
|
||||
def toString(this, args):
|
||||
if Type(this) != 'Object':
|
||||
raise MakeError('TypeError',
|
||||
'Error.prototype.toString called on non-object')
|
||||
name = this.get('name')
|
||||
name = u'Error' if is_undefined(name) else to_string(name)
|
||||
msg = this.get('message')
|
||||
msg = '' if is_undefined(msg) else to_string(msg)
|
||||
return name + (name and msg and ': ') + msg
|
||||
@@ -1,61 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
# python 3 support
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
# todo fix apply and bind
|
||||
|
||||
|
||||
class FunctionPrototype:
|
||||
def toString(this, args):
|
||||
if not is_callable(this):
|
||||
raise MakeError('TypeError',
|
||||
'Function.prototype.toString is not generic')
|
||||
|
||||
args = u', '.join(map(unicode, this.params))
|
||||
return u'function %s(%s) { [native code] }' % (this.name if this.name
|
||||
else u'', args)
|
||||
|
||||
def call(this, args):
|
||||
if not is_callable(this):
|
||||
raise MakeError('TypeError',
|
||||
'Function.prototype.call is not generic')
|
||||
_this = get_arg(args, 0)
|
||||
_args = tuple(args)[1:]
|
||||
return this.call(_this, _args)
|
||||
|
||||
def apply(this, args):
|
||||
if not is_callable(this):
|
||||
raise MakeError('TypeError',
|
||||
'Function.prototype.apply is not generic')
|
||||
_args = get_arg(args, 1)
|
||||
if not is_object(_args):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'argList argument to Function.prototype.apply must an Object')
|
||||
_this = get_arg(args, 0)
|
||||
return this.call(_this, js_array_to_tuple(_args))
|
||||
|
||||
def bind(this, args):
|
||||
if not is_callable(this):
|
||||
raise MakeError('TypeError',
|
||||
'Function.prototype.bind is not generic')
|
||||
bound_this = get_arg(args, 0)
|
||||
bound_args = tuple(args)[1:]
|
||||
|
||||
def bound(dummy_this, extra_args):
|
||||
return this.call(bound_this, bound_args + tuple(extra_args))
|
||||
|
||||
js_bound = args.space.NewFunction(bound, this.ctx, (), u'', False, ())
|
||||
js_bound.put(u'length',
|
||||
float(max(len(this.params) - len(bound_args), 0.)))
|
||||
js_bound.put(u'name', u'boundFunc')
|
||||
return js_bound
|
||||
@@ -1,205 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from ..operations import strict_equality_op
|
||||
import json
|
||||
|
||||
indent = ''
|
||||
# python 3 support
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
|
||||
def parse(this, args):
|
||||
text, reviver = get_arg(args, 0), get_arg(args, 1)
|
||||
s = to_string(text)
|
||||
try:
|
||||
unfiltered = json.loads(s)
|
||||
except:
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'JSON.parse could not parse JSON string - Invalid syntax')
|
||||
unfiltered = to_js(unfiltered, args.space)
|
||||
if is_callable(reviver):
|
||||
root = args.space.ConstructObject({'': unfiltered})
|
||||
return walk(root, '', reviver)
|
||||
else:
|
||||
return unfiltered
|
||||
|
||||
|
||||
def stringify(this, args):
|
||||
global indent
|
||||
value, replacer, space = get_arg(args, 0), get_arg(args, 1), get_arg(
|
||||
args, 2)
|
||||
stack = set([])
|
||||
indent = ''
|
||||
property_list = replacer_function = undefined
|
||||
if is_object(replacer):
|
||||
if is_callable(replacer):
|
||||
replacer_function = replacer
|
||||
elif replacer.Class == 'Array':
|
||||
property_list = []
|
||||
for e in replacer:
|
||||
v = replacer[e]
|
||||
item = undefined
|
||||
typ = Type(v)
|
||||
if typ == 'Number':
|
||||
item = to_string(v)
|
||||
elif typ == 'String':
|
||||
item = v
|
||||
elif typ == 'Object':
|
||||
if GetClass(v) in ('String', 'Number'):
|
||||
item = to_string(v)
|
||||
if not is_undefined(item) and item not in property_list:
|
||||
property_list.append(item)
|
||||
if is_object(space):
|
||||
if GetClass(space) == 'Number':
|
||||
space = to_number(space)
|
||||
elif GetClass(space) == 'String':
|
||||
space = to_string(space)
|
||||
if Type(space) == 'Number':
|
||||
space = min(10, to_int(space))
|
||||
gap = max(int(space), 0) * ' '
|
||||
elif Type(space) == 'String':
|
||||
gap = space[:10]
|
||||
else:
|
||||
gap = ''
|
||||
return Str('', args.space.ConstructObject({
|
||||
'': value
|
||||
}), replacer_function, property_list, gap, stack, space)
|
||||
|
||||
|
||||
def Str(key, holder, replacer_function, property_list, gap, stack, space):
|
||||
value = holder.get(key)
|
||||
if is_object(value):
|
||||
to_json = value.get('toJSON')
|
||||
if is_callable(to_json):
|
||||
value = to_json.call(value, (key, ))
|
||||
if not is_undefined(replacer_function):
|
||||
value = replacer_function.call(holder, (key, value))
|
||||
|
||||
if is_object(value):
|
||||
if value.Class == 'String':
|
||||
value = to_string(value)
|
||||
elif value.Class == 'Number':
|
||||
value = to_number(value)
|
||||
elif value.Class == 'Boolean':
|
||||
value = to_boolean(value)
|
||||
typ = Type(value)
|
||||
if is_null(value):
|
||||
return 'null'
|
||||
elif typ == 'Boolean':
|
||||
return 'true' if value else 'false'
|
||||
elif typ == 'String':
|
||||
return Quote(value)
|
||||
elif typ == 'Number':
|
||||
if not is_infinity(value):
|
||||
return to_string(value)
|
||||
return 'null'
|
||||
if is_object(value) and not is_callable(value):
|
||||
if value.Class == 'Array':
|
||||
return ja(value, stack, gap, property_list, replacer_function,
|
||||
space)
|
||||
else:
|
||||
return jo(value, stack, gap, property_list, replacer_function,
|
||||
space)
|
||||
return undefined
|
||||
|
||||
|
||||
def jo(value, stack, gap, property_list, replacer_function, space):
|
||||
global indent
|
||||
if value in stack:
|
||||
raise MakeError('TypeError', 'Converting circular structure to JSON')
|
||||
stack.add(value)
|
||||
stepback = indent
|
||||
indent += gap
|
||||
if not is_undefined(property_list):
|
||||
k = property_list
|
||||
else:
|
||||
k = [unicode(e) for e, d in value.own.items() if d.get('enumerable')]
|
||||
partial = []
|
||||
for p in k:
|
||||
str_p = Str(p, value, replacer_function, property_list, gap, stack,
|
||||
space)
|
||||
if not is_undefined(str_p):
|
||||
member = json.dumps(p) + ':' + (
|
||||
' ' if gap else
|
||||
'') + str_p # todo not sure here - what space character?
|
||||
partial.append(member)
|
||||
if not partial:
|
||||
final = '{}'
|
||||
else:
|
||||
if not gap:
|
||||
final = '{%s}' % ','.join(partial)
|
||||
else:
|
||||
sep = ',\n' + indent
|
||||
properties = sep.join(partial)
|
||||
final = '{\n' + indent + properties + '\n' + stepback + '}'
|
||||
stack.remove(value)
|
||||
indent = stepback
|
||||
return final
|
||||
|
||||
|
||||
def ja(value, stack, gap, property_list, replacer_function, space):
|
||||
global indent
|
||||
if value in stack:
|
||||
raise MakeError('TypeError', 'Converting circular structure to JSON')
|
||||
stack.add(value)
|
||||
stepback = indent
|
||||
indent += gap
|
||||
partial = []
|
||||
length = js_arr_length(value)
|
||||
for index in xrange(length):
|
||||
index = unicode(index)
|
||||
str_index = Str(index, value, replacer_function, property_list, gap,
|
||||
stack, space)
|
||||
if is_undefined(str_index):
|
||||
partial.append('null')
|
||||
else:
|
||||
partial.append(str_index)
|
||||
if not partial:
|
||||
final = '[]'
|
||||
else:
|
||||
if not gap:
|
||||
final = '[%s]' % ','.join(partial)
|
||||
else:
|
||||
sep = ',\n' + indent
|
||||
properties = sep.join(partial)
|
||||
final = '[\n' + indent + properties + '\n' + stepback + ']'
|
||||
stack.remove(value)
|
||||
indent = stepback
|
||||
return final
|
||||
|
||||
|
||||
def Quote(string):
|
||||
return json.dumps(string)
|
||||
|
||||
|
||||
def to_js(d, _args_space):
|
||||
return convert_to_js_type(d, _args_space)
|
||||
|
||||
|
||||
def walk(holder, name, reviver):
|
||||
val = holder.get(name)
|
||||
if GetClass(val) == 'Array':
|
||||
for i in xrange(js_arr_length(val)):
|
||||
i = unicode(i)
|
||||
new_element = walk(val, i, reviver)
|
||||
if is_undefined(new_element):
|
||||
val.delete(i)
|
||||
else:
|
||||
new_element.put(i, new_element)
|
||||
elif is_object(val):
|
||||
for key in [
|
||||
unicode(e) for e, d in val.own.items() if d.get('enumerable')
|
||||
]:
|
||||
new_element = walk(val, key, reviver)
|
||||
if is_undefined(new_element):
|
||||
val.delete(key)
|
||||
else:
|
||||
val.put(key, new_element)
|
||||
return reviver.call(holder, (name, val))
|
||||
@@ -1,163 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
RADIX_SYMBOLS = {
|
||||
0: '0',
|
||||
1: '1',
|
||||
2: '2',
|
||||
3: '3',
|
||||
4: '4',
|
||||
5: '5',
|
||||
6: '6',
|
||||
7: '7',
|
||||
8: '8',
|
||||
9: '9',
|
||||
10: 'a',
|
||||
11: 'b',
|
||||
12: 'c',
|
||||
13: 'd',
|
||||
14: 'e',
|
||||
15: 'f',
|
||||
16: 'g',
|
||||
17: 'h',
|
||||
18: 'i',
|
||||
19: 'j',
|
||||
20: 'k',
|
||||
21: 'l',
|
||||
22: 'm',
|
||||
23: 'n',
|
||||
24: 'o',
|
||||
25: 'p',
|
||||
26: 'q',
|
||||
27: 'r',
|
||||
28: 's',
|
||||
29: 't',
|
||||
30: 'u',
|
||||
31: 'v',
|
||||
32: 'w',
|
||||
33: 'x',
|
||||
34: 'y',
|
||||
35: 'z'
|
||||
}
|
||||
|
||||
|
||||
def to_str_rep(num):
|
||||
if is_nan(num):
|
||||
return 'NaN'
|
||||
elif is_infinity(num):
|
||||
sign = '-' if num < 0 else ''
|
||||
return sign + 'Infinity'
|
||||
elif int(num) == num: # dont print .0
|
||||
return unicode(int(num))
|
||||
return unicode(num) # todo: Make it 100% consistent with Node
|
||||
|
||||
|
||||
class NumberPrototype:
|
||||
def toString(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError('TypeError',
|
||||
'Number.prototype.valueOf is not generic')
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
radix = get_arg(args, 0)
|
||||
if is_undefined(radix):
|
||||
return to_str_rep(this)
|
||||
r = to_int(radix)
|
||||
if r == 10:
|
||||
return to_str_rep(this)
|
||||
if r not in xrange(2, 37) or radix != r:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Number.prototype.toString() radix argument must be an integer between 2 and 36'
|
||||
)
|
||||
num = to_int(this)
|
||||
if num < 0:
|
||||
num = -num
|
||||
sign = '-'
|
||||
else:
|
||||
sign = ''
|
||||
res = ''
|
||||
while num:
|
||||
s = RADIX_SYMBOLS[num % r]
|
||||
num = num // r
|
||||
res = s + res
|
||||
return sign + (res if res else '0')
|
||||
|
||||
def valueOf(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError('TypeError',
|
||||
'Number.prototype.valueOf is not generic')
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
return this
|
||||
|
||||
def toFixed(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toFixed called on incompatible receiver')
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
fractionDigits = get_arg(args, 0)
|
||||
digs = to_int(fractionDigits)
|
||||
if digs < 0 or digs > 20:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'toFixed() digits argument must be between 0 and 20')
|
||||
elif is_infinity(this):
|
||||
return 'Infinity' if this > 0 else '-Infinity'
|
||||
elif is_nan(this):
|
||||
return 'NaN'
|
||||
return format(this, '-.%df' % digs)
|
||||
|
||||
def toExponential(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toExponential called on incompatible receiver'
|
||||
)
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
fractionDigits = get_arg(args, 0)
|
||||
digs = to_int(fractionDigits)
|
||||
if digs < 0 or digs > 20:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'toFixed() digits argument must be between 0 and 20')
|
||||
elif is_infinity(this):
|
||||
return 'Infinity' if this > 0 else '-Infinity'
|
||||
elif is_nan(this):
|
||||
return 'NaN'
|
||||
return format(this, '-.%de' % digs)
|
||||
|
||||
def toPrecision(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toPrecision called on incompatible receiver')
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
precision = get_arg(args, 0)
|
||||
if is_undefined(precision):
|
||||
return to_string(this)
|
||||
prec = to_int(precision)
|
||||
if is_nan(this):
|
||||
return 'NaN'
|
||||
elif is_infinity(this):
|
||||
return 'Infinity' if this > 0 else '-Infinity'
|
||||
digs = prec - len(str(int(this)))
|
||||
if digs >= 0:
|
||||
return format(this, '-.%df' % digs)
|
||||
else:
|
||||
return format(this, '-.%df' % (prec - 1))
|
||||
|
||||
|
||||
NumberPrototype.toLocaleString = NumberPrototype.toString
|
||||
@@ -1,48 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
class ObjectPrototype:
|
||||
def toString(this, args):
|
||||
if type(this) == UNDEFINED_TYPE:
|
||||
return u'[object Undefined]'
|
||||
elif type(this) == NULL_TYPE:
|
||||
return u'[object Null]'
|
||||
return u'[object %s]' % GetClass(to_object(this, args.space))
|
||||
|
||||
def valueOf(this, args):
|
||||
return to_object(this, args.space)
|
||||
|
||||
def toLocaleString(this, args):
|
||||
o = to_object(this, args.space)
|
||||
toString = o.get(u'toString')
|
||||
if not is_callable(toString):
|
||||
raise MakeError('TypeError', 'toString of this is not callcable')
|
||||
else:
|
||||
return toString.call(this, args)
|
||||
|
||||
def hasOwnProperty(this, args):
|
||||
prop = get_arg(args, 0)
|
||||
o = to_object(this, args.space)
|
||||
return o.get_own_property(to_string(prop)) is not None
|
||||
|
||||
def isPrototypeOf(this, args):
|
||||
# a bit stupid specification because of object conversion that will cause invalid values for primitives
|
||||
# for example Object.prototype.isPrototypeOf.call((5).__proto__, 5) gives false
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
return False
|
||||
o = to_object(this, args.space)
|
||||
while 1:
|
||||
obj = obj.prototype
|
||||
if obj is None or is_null(obj):
|
||||
return False
|
||||
if obj is o:
|
||||
return True
|
||||
|
||||
def propertyIsEnumerable(this, args):
|
||||
prop = get_arg(args, 0)
|
||||
o = to_object(this, args.space)
|
||||
cand = o.own.get(to_string(prop))
|
||||
return cand is not None and cand.get('enumerable')
|
||||
@@ -1,56 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
class RegExpPrototype:
|
||||
def toString(this, args):
|
||||
flags = u''
|
||||
try:
|
||||
if this.glob:
|
||||
flags += u'g'
|
||||
if this.ignore_case:
|
||||
flags += u'i'
|
||||
if this.multiline:
|
||||
flags += u'm'
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
v = this.value if this.value else u'(?:)'
|
||||
except:
|
||||
v = u'(?:)'
|
||||
return u'/%s/' % v + flags
|
||||
|
||||
def test(this, args):
|
||||
string = get_arg(args, 0)
|
||||
return RegExpExec(this, string, args.space) is not null
|
||||
|
||||
def _exec(
|
||||
this, args
|
||||
): # will be changed to exec in base.py. cant name it exec here...
|
||||
string = get_arg(args, 0)
|
||||
return RegExpExec(this, string, args.space)
|
||||
|
||||
|
||||
def RegExpExec(this, string, space):
|
||||
if GetClass(this) != 'RegExp':
|
||||
raise MakeError('TypeError', 'RegExp.prototype.exec is not generic!')
|
||||
string = to_string(string)
|
||||
length = len(string)
|
||||
i = to_int(this.get('lastIndex')) if this.glob else 0
|
||||
matched = False
|
||||
while not matched:
|
||||
if i < 0 or i > length:
|
||||
this.put('lastIndex', 0.)
|
||||
return null
|
||||
matched = this.match(string, i)
|
||||
i += 1
|
||||
start, end = matched.span() #[0]+i-1, matched.span()[1]+i-1
|
||||
if this.glob:
|
||||
this.put('lastIndex', float(end))
|
||||
arr = convert_to_js_type(
|
||||
[matched.group()] + list(matched.groups()), space=space)
|
||||
arr.put('index', float(start))
|
||||
arr.put('input', unicode(string))
|
||||
return arr
|
||||
@@ -1,323 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from jsregexp import RegExpExec
|
||||
|
||||
DIGS = set(u'0123456789')
|
||||
WHITE = u"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
|
||||
|
||||
|
||||
def replacement_template(rep, source, span, npar):
|
||||
"""Takes the replacement template and some info about the match and returns filled template
|
||||
"""
|
||||
n = 0
|
||||
res = ''
|
||||
while n < len(rep) - 1:
|
||||
char = rep[n]
|
||||
if char == '$':
|
||||
if rep[n + 1] == '$':
|
||||
res += '$'
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] == '`':
|
||||
# replace with string that is BEFORE match
|
||||
res += source[:span[0]]
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] == '\'':
|
||||
# replace with string that is AFTER match
|
||||
res += source[span[1]:]
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] in DIGS:
|
||||
dig = rep[n + 1]
|
||||
if n + 2 < len(rep) and rep[n + 2] in DIGS:
|
||||
dig += rep[n + 2]
|
||||
num = int(dig)
|
||||
# we will not do any replacements if we dont have this npar or dig is 0
|
||||
if not num or num > len(npar):
|
||||
res += '$' + dig
|
||||
else:
|
||||
# None - undefined has to be replaced with ''
|
||||
res += npar[num - 1] if npar[num - 1] else ''
|
||||
n += 1 + len(dig)
|
||||
continue
|
||||
res += char
|
||||
n += 1
|
||||
if n < len(rep):
|
||||
res += rep[-1]
|
||||
return res
|
||||
|
||||
|
||||
###################################################
|
||||
|
||||
|
||||
class StringPrototype:
|
||||
def toString(this, args):
|
||||
if GetClass(this) != 'String':
|
||||
raise MakeError('TypeError',
|
||||
'String.prototype.toString is not generic')
|
||||
if type(this) == unicode:
|
||||
return this
|
||||
assert type(this.value) == unicode
|
||||
return this.value
|
||||
|
||||
def valueOf(this, args):
|
||||
if GetClass(this) != 'String':
|
||||
raise MakeError('TypeError',
|
||||
'String.prototype.valueOf is not generic')
|
||||
if type(this) == unicode:
|
||||
return this
|
||||
assert type(this.value) == unicode
|
||||
return this.value
|
||||
|
||||
def charAt(this, args):
|
||||
cok(this)
|
||||
pos = to_int(get_arg(args, 0))
|
||||
s = to_string(this)
|
||||
if 0 <= pos < len(s):
|
||||
return s[pos]
|
||||
return u''
|
||||
|
||||
def charCodeAt(this, args):
|
||||
cok(this)
|
||||
pos = to_int(get_arg(args, 0))
|
||||
s = to_string(this)
|
||||
if 0 <= pos < len(s):
|
||||
return float(ord(s[pos]))
|
||||
return NaN
|
||||
|
||||
def concat(this, args):
|
||||
cok(this)
|
||||
return to_string(this) + u''.join(map(to_string, args))
|
||||
|
||||
def indexOf(this, args):
|
||||
cok(this)
|
||||
search = to_string(get_arg(args, 0))
|
||||
pos = to_int(get_arg(args, 1))
|
||||
s = to_string(this)
|
||||
return float(s.find(search, min(max(pos, 0), len(s))))
|
||||
|
||||
def lastIndexOf(this, args):
|
||||
cok(this)
|
||||
search = to_string(get_arg(args, 0))
|
||||
pos = get_arg(args, 1)
|
||||
s = to_string(this)
|
||||
pos = 10**12 if is_nan(pos) else to_int(pos)
|
||||
return float(s.rfind(search, 0, min(max(pos, 0) + 1, len(s))))
|
||||
|
||||
def localeCompare(this, args):
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
that = to_string(get_arg(args, 0))
|
||||
if s < that:
|
||||
return -1.
|
||||
elif s > that:
|
||||
return 1.
|
||||
return 0.
|
||||
|
||||
def match(this, args):
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
regexp = get_arg(args, 0)
|
||||
r = args.space.NewRegExp(
|
||||
regexp, '') if GetClass(regexp) != 'RegExp' else regexp
|
||||
if not r.glob:
|
||||
return RegExpExec(r, s, space=args.space)
|
||||
r.put('lastIndex', float(0))
|
||||
found = []
|
||||
previous_last_index = 0
|
||||
last_match = True
|
||||
while last_match:
|
||||
result = RegExpExec(r, s, space=args.space)
|
||||
if is_null(result):
|
||||
last_match = False
|
||||
else:
|
||||
this_index = r.get('lastIndex')
|
||||
if this_index == previous_last_index:
|
||||
r.put('lastIndex', float(this_index + 1))
|
||||
previous_last_index += 1
|
||||
else:
|
||||
previous_last_index = this_index
|
||||
matchStr = result.get('0')
|
||||
found.append(matchStr)
|
||||
if not found:
|
||||
return null
|
||||
return args.space.ConstructArray(found)
|
||||
|
||||
def replace(this, args):
|
||||
# VERY COMPLICATED. to check again.
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
searchValue = get_arg(args, 0)
|
||||
replaceValue = get_arg(args, 1)
|
||||
res = ''
|
||||
if not is_callable(replaceValue):
|
||||
replaceValue = to_string(replaceValue)
|
||||
func = False
|
||||
else:
|
||||
func = True
|
||||
# Replace all ( global )
|
||||
if GetClass(searchValue) == 'RegExp' and searchValue.glob:
|
||||
last = 0
|
||||
for e in re.finditer(searchValue.pat, s):
|
||||
res += s[last:e.span()[0]]
|
||||
if func:
|
||||
# prepare arguments for custom func (replaceValue)
|
||||
call_args = (e.group(), ) + e.groups() + (e.span()[1], s)
|
||||
# convert all types to JS before Js bytecode call...
|
||||
res += to_string(
|
||||
replaceValue.call(
|
||||
this, ensure_js_types(call_args,
|
||||
space=args.space)))
|
||||
else:
|
||||
res += replacement_template(replaceValue, s, e.span(),
|
||||
e.groups())
|
||||
last = e.span()[1]
|
||||
res += s[last:]
|
||||
return res
|
||||
elif GetClass(searchValue) == 'RegExp':
|
||||
e = re.search(searchValue.pat, s)
|
||||
if e is None:
|
||||
return s
|
||||
span = e.span()
|
||||
pars = e.groups()
|
||||
match = e.group()
|
||||
else:
|
||||
match = to_string(searchValue)
|
||||
ind = s.find(match)
|
||||
if ind == -1:
|
||||
return s
|
||||
span = ind, ind + len(match)
|
||||
pars = ()
|
||||
res = s[:span[0]]
|
||||
if func:
|
||||
call_args = (match, ) + pars + (span[1], s)
|
||||
# convert all types to JS before Js bytecode call...
|
||||
res += to_string(
|
||||
replaceValue.call(this,
|
||||
ensure_js_types(call_args,
|
||||
space=args.space)))
|
||||
else:
|
||||
res += replacement_template(replaceValue, s, span, pars)
|
||||
res += s[span[1]:]
|
||||
return res
|
||||
|
||||
def search(this, args):
|
||||
cok(this)
|
||||
string = to_string(this)
|
||||
regexp = get_arg(args, 0)
|
||||
if GetClass(regexp) == 'RegExp':
|
||||
rx = regexp
|
||||
else:
|
||||
rx = args.space.NewRegExp(regexp, '')
|
||||
res = re.search(rx.pat, string)
|
||||
if res is not None:
|
||||
return float(res.span()[0])
|
||||
return -1.
|
||||
|
||||
def slice(this, args):
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
start = to_int(get_arg(args, 0))
|
||||
length = len(s)
|
||||
end = get_arg(args, 1)
|
||||
end = length if is_undefined(end) else to_int(end)
|
||||
#From = max(length+start, 0) if start<0 else min(length, start)
|
||||
#To = max(length+end, 0) if end<0 else min(length, end)
|
||||
return s[start:end]
|
||||
|
||||
def split(this, args):
|
||||
# its a bit different from re.split!
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
separator = get_arg(args, 0)
|
||||
limit = get_arg(args, 1)
|
||||
lim = 2**32 - 1 if is_undefined(limit) else to_uint32(limit)
|
||||
if not lim:
|
||||
return args.space.ConstructArray([])
|
||||
if is_undefined(separator):
|
||||
return args.space.ConstructArray([s])
|
||||
len_s = len(s)
|
||||
res = []
|
||||
R = separator if GetClass(separator) == 'RegExp' else to_string(
|
||||
separator)
|
||||
if not len_s:
|
||||
if SplitMatch(s, 0, R) is None:
|
||||
return args.space.ConstructArray([s])
|
||||
return args.space.ConstructArray([])
|
||||
p = q = 0
|
||||
while q != len_s:
|
||||
e, cap = SplitMatch(s, q, R)
|
||||
if e is None or e == p:
|
||||
q += 1
|
||||
continue
|
||||
res.append(s[p:q])
|
||||
p = q = e
|
||||
if len(res) == lim:
|
||||
return args.space.ConstructArray(res)
|
||||
for element in cap:
|
||||
res.append(element)
|
||||
if len(res) == lim:
|
||||
return args.space.ConstructArray(res)
|
||||
res.append(s[p:])
|
||||
return args.space.ConstructArray(res)
|
||||
|
||||
def substring(this, args):
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
start = to_int(get_arg(args, 0))
|
||||
length = len(s)
|
||||
end = get_arg(args, 1)
|
||||
end = length if is_undefined(end) else to_int(end)
|
||||
fstart = min(max(start, 0), length)
|
||||
fend = min(max(end, 0), length)
|
||||
return s[min(fstart, fend):max(fstart, fend)]
|
||||
|
||||
def substr(this, args):
|
||||
cok(this)
|
||||
#I hate this function and its description in specification
|
||||
r1 = to_string(this)
|
||||
r2 = to_int(get_arg(args, 0))
|
||||
length = get_arg(args, 1)
|
||||
r3 = 10**20 if is_undefined(length) else to_int(length)
|
||||
r4 = len(r1)
|
||||
r5 = r2 if r2 >= 0 else max(0, r2 + r4)
|
||||
r6 = min(max(r3, 0), r4 - r5)
|
||||
if r6 <= 0:
|
||||
return ''
|
||||
return r1[r5:r5 + r6]
|
||||
|
||||
def toLowerCase(this, args):
|
||||
cok(this)
|
||||
return to_string(this).lower()
|
||||
|
||||
def toLocaleLowerCase(this, args):
|
||||
cok(this)
|
||||
return to_string(this).lower()
|
||||
|
||||
def toUpperCase(this, args):
|
||||
cok(this)
|
||||
return to_string(this).upper()
|
||||
|
||||
def toLocaleUpperCase(this, args):
|
||||
cok(this)
|
||||
return to_string(this).upper()
|
||||
|
||||
def trim(this, args):
|
||||
cok(this)
|
||||
return to_string(this).strip(WHITE)
|
||||
|
||||
|
||||
def SplitMatch(s, q, R):
|
||||
# s is Py String to match, q is the py int match start and R is Js RegExp or String.
|
||||
if GetClass(R) == 'RegExp':
|
||||
res = R.match(s, q)
|
||||
return (None, ()) if res is None else (res.span()[1], res.groups())
|
||||
# R is just a string
|
||||
if s[q:].startswith(R):
|
||||
return q + len(R), ()
|
||||
return None, ()
|
||||
@@ -1,149 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
RADIX_CHARS = {
|
||||
'1': 1,
|
||||
'0': 0,
|
||||
'3': 3,
|
||||
'2': 2,
|
||||
'5': 5,
|
||||
'4': 4,
|
||||
'7': 7,
|
||||
'6': 6,
|
||||
'9': 9,
|
||||
'8': 8,
|
||||
'a': 10,
|
||||
'c': 12,
|
||||
'b': 11,
|
||||
'e': 14,
|
||||
'd': 13,
|
||||
'g': 16,
|
||||
'f': 15,
|
||||
'i': 18,
|
||||
'h': 17,
|
||||
'k': 20,
|
||||
'j': 19,
|
||||
'm': 22,
|
||||
'l': 21,
|
||||
'o': 24,
|
||||
'n': 23,
|
||||
'q': 26,
|
||||
'p': 25,
|
||||
's': 28,
|
||||
'r': 27,
|
||||
'u': 30,
|
||||
't': 29,
|
||||
'w': 32,
|
||||
'v': 31,
|
||||
'y': 34,
|
||||
'x': 33,
|
||||
'z': 35,
|
||||
'A': 10,
|
||||
'C': 12,
|
||||
'B': 11,
|
||||
'E': 14,
|
||||
'D': 13,
|
||||
'G': 16,
|
||||
'F': 15,
|
||||
'I': 18,
|
||||
'H': 17,
|
||||
'K': 20,
|
||||
'J': 19,
|
||||
'M': 22,
|
||||
'L': 21,
|
||||
'O': 24,
|
||||
'N': 23,
|
||||
'Q': 26,
|
||||
'P': 25,
|
||||
'S': 28,
|
||||
'R': 27,
|
||||
'U': 30,
|
||||
'T': 29,
|
||||
'W': 32,
|
||||
'V': 31,
|
||||
'Y': 34,
|
||||
'X': 33,
|
||||
'Z': 35
|
||||
}
|
||||
|
||||
# parseFloat
|
||||
# parseInt
|
||||
# isFinite
|
||||
# isNaN
|
||||
|
||||
|
||||
def parseInt(this, args):
|
||||
string, radix = get_arg(args, 0), get_arg(args, 1)
|
||||
string = to_string(string).lstrip()
|
||||
sign = 1
|
||||
if string and string[0] in ('+', '-'):
|
||||
if string[0] == '-':
|
||||
sign = -1
|
||||
string = string[1:]
|
||||
r = to_int32(radix)
|
||||
strip_prefix = True
|
||||
if r:
|
||||
if r < 2 or r > 36:
|
||||
return NaN
|
||||
if r != 16:
|
||||
strip_prefix = False
|
||||
else:
|
||||
r = 10
|
||||
if strip_prefix:
|
||||
if len(string) >= 2 and string[:2] in ('0x', '0X'):
|
||||
string = string[2:]
|
||||
r = 16
|
||||
n = 0
|
||||
num = 0
|
||||
while n < len(string):
|
||||
cand = RADIX_CHARS.get(string[n])
|
||||
if cand is None or not cand < r:
|
||||
break
|
||||
num = cand + num * r
|
||||
n += 1
|
||||
if not n:
|
||||
return NaN
|
||||
return float(sign * num)
|
||||
|
||||
|
||||
def parseFloat(this, args):
|
||||
string = get_arg(args, 0)
|
||||
string = to_string(string).strip()
|
||||
sign = 1
|
||||
if string and string[0] in ('+', '-'):
|
||||
if string[0] == '-':
|
||||
sign = -1
|
||||
string = string[1:]
|
||||
num = None
|
||||
length = 1
|
||||
max_len = None
|
||||
failed = 0
|
||||
while length <= len(string):
|
||||
try:
|
||||
num = float(string[:length])
|
||||
max_len = length
|
||||
failed = 0
|
||||
except:
|
||||
failed += 1
|
||||
if failed > 4: # cant be a number anymore
|
||||
break
|
||||
length += 1
|
||||
if num is None:
|
||||
return NaN
|
||||
return sign * float(string[:max_len])
|
||||
|
||||
|
||||
def isNaN(this, args):
|
||||
number = get_arg(args, 0)
|
||||
if is_nan(to_number(number)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isFinite(this, args):
|
||||
number = get_arg(args, 0)
|
||||
num = to_number(number)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return False
|
||||
return True
|
||||
@@ -1,32 +0,0 @@
|
||||
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 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()
|
||||
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)
|
||||
@@ -1,133 +0,0 @@
|
||||
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')
|
||||
NaN = float('nan')
|
||||
|
||||
UNDEFINED_TYPE = PyJsUndefined
|
||||
NULL_TYPE = PyJsNull
|
||||
STRING_TYPE = unicode if six.PY2 else str
|
||||
NUMBER_TYPE = float
|
||||
BOOLEAN_TYPE = bool
|
||||
|
||||
# exactly 5 simplexes!
|
||||
PRIMITIVES = frozenset(
|
||||
[UNDEFINED_TYPE, NULL_TYPE, STRING_TYPE, NUMBER_TYPE, BOOLEAN_TYPE])
|
||||
|
||||
TYPE_NAMES = {
|
||||
UNDEFINED_TYPE: 'Undefined',
|
||||
NULL_TYPE: 'Null',
|
||||
STRING_TYPE: 'String',
|
||||
NUMBER_TYPE: 'Number',
|
||||
BOOLEAN_TYPE: 'Boolean',
|
||||
}
|
||||
|
||||
|
||||
def Type(x):
|
||||
# Any -> Str
|
||||
return TYPE_NAMES.get(type(x), 'Object')
|
||||
|
||||
|
||||
def GetClass(x):
|
||||
# Any -> Str
|
||||
cand = TYPE_NAMES.get(type(x))
|
||||
if cand is None:
|
||||
return x.Class
|
||||
return cand
|
||||
|
||||
|
||||
def is_undefined(self):
|
||||
return self is undefined
|
||||
|
||||
|
||||
def is_null(self):
|
||||
return self is null
|
||||
|
||||
|
||||
def is_primitive(self):
|
||||
return type(self) in PRIMITIVES
|
||||
|
||||
|
||||
def is_object(self):
|
||||
return not is_primitive(self)
|
||||
|
||||
|
||||
def is_callable(self):
|
||||
return hasattr(self, 'call')
|
||||
|
||||
|
||||
def is_infinity(self):
|
||||
return self == 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))
|
||||
|
||||
|
||||
class JsException(Exception):
|
||||
def __init__(self, typ=None, message=None, throw=None):
|
||||
if typ is None and message is None and throw is None:
|
||||
# it means its the trasnlator based error (old format), do nothing
|
||||
self._translator_based = True
|
||||
else:
|
||||
assert throw is None or (typ is None
|
||||
and message is None), (throw, typ,
|
||||
message)
|
||||
self._translator_based = False
|
||||
self.typ = typ
|
||||
self.message = message
|
||||
self.throw = throw
|
||||
|
||||
def get_thrown_value(self, space):
|
||||
if self.throw is not None:
|
||||
return self.throw
|
||||
else:
|
||||
return space.NewError(self.typ, self.message)
|
||||
|
||||
def __str__(self):
|
||||
if self._translator_based:
|
||||
if self.mes.Class == 'Error':
|
||||
return self.mes.callprop('toString').value
|
||||
else:
|
||||
return self.mes.to_string().value
|
||||
else:
|
||||
if self.throw is not None:
|
||||
from conversions import to_string
|
||||
return to_string(self.throw)
|
||||
else:
|
||||
return self.typ + ': ' + self.message
|
||||
|
||||
|
||||
def MakeError(typ, message=u'no info', throw=None):
|
||||
return JsException(typ,
|
||||
unicode(message) if message is not None else message,
|
||||
throw)
|
||||
|
||||
|
||||
def value_from_js_exception(js_exception, space):
|
||||
if js_exception.throw is not None:
|
||||
return js_exception.throw
|
||||
else:
|
||||
return space.NewError(js_exception.typ, js_exception.message)
|
||||
@@ -1,92 +0,0 @@
|
||||
from base import *
|
||||
from simplex import *
|
||||
|
||||
|
||||
class Space(object):
|
||||
def __init__(self):
|
||||
self.GlobalObj = None
|
||||
self.ctx = None
|
||||
self.byte_generator = None
|
||||
|
||||
self.Number = None
|
||||
self.String = None
|
||||
self.Boolean = None
|
||||
self.RegExp = None
|
||||
self.Object = None
|
||||
self.Array = None
|
||||
self.Function = None
|
||||
|
||||
self.BooleanPrototype = None
|
||||
self.NumberPrototype = None
|
||||
self.StringPrototype = None
|
||||
|
||||
self.FunctionPrototype = None
|
||||
self.ArrayPrototype = None
|
||||
self.RegExpPrototype = None
|
||||
self.DatePrototype = None
|
||||
self.ObjectPrototype = None
|
||||
|
||||
self.ErrorPrototype = None
|
||||
self.EvalErrorPrototype = None
|
||||
self.RangeErrorPrototype = None
|
||||
self.ReferenceErrorPrototype = None
|
||||
self.SyntaxErrorPrototype = None
|
||||
self.TypeErrorPrototype = None
|
||||
self.URIErrorPrototype = None
|
||||
|
||||
self.interpreter = None
|
||||
|
||||
@property
|
||||
def ERROR_TYPES(self):
|
||||
return {
|
||||
'Error': self.ErrorPrototype,
|
||||
'EvalError': self.EvalErrorPrototype,
|
||||
'RangeError': self.RangeErrorPrototype,
|
||||
'ReferenceError': self.ReferenceErrorPrototype,
|
||||
'SyntaxError': self.SyntaxErrorPrototype,
|
||||
'TypeError': self.TypeErrorPrototype,
|
||||
'URIError': self.URIErrorPrototype,
|
||||
}
|
||||
|
||||
def get_global_environment(self):
|
||||
return self.GlobalCtx.variable_environment()
|
||||
|
||||
def NewObject(self):
|
||||
return PyJsObject(self.ObjectPrototype)
|
||||
|
||||
def NewFunction(self, code, ctx, params, name, is_declaration,
|
||||
definitions):
|
||||
return PyJsFunction(
|
||||
code,
|
||||
ctx,
|
||||
params,
|
||||
name,
|
||||
self,
|
||||
is_declaration,
|
||||
definitions,
|
||||
prototype=self.FunctionPrototype)
|
||||
|
||||
def NewDate(self, value):
|
||||
return PyJsDate(value, self.DatePrototype)
|
||||
|
||||
def NewArray(self, length=0):
|
||||
return PyJsArray(length, self.ArrayPrototype)
|
||||
|
||||
def NewError(self, typ, message):
|
||||
return PyJsError(message, self.ERROR_TYPES[typ])
|
||||
|
||||
def NewRegExp(self, body, flags):
|
||||
return PyJsRegExp(body, flags, self.RegExpPrototype)
|
||||
|
||||
def ConstructArray(self, py_arr):
|
||||
''' note py_arr elems are NOT converted to PyJs types!'''
|
||||
arr = self.NewArray(len(py_arr))
|
||||
arr._init(py_arr)
|
||||
return arr
|
||||
|
||||
def ConstructObject(self, py_obj):
|
||||
''' note py_obj items are NOT converted to PyJs types! '''
|
||||
obj = self.NewObject()
|
||||
for k, v in py_obj.items():
|
||||
obj.put(unicode(k), v)
|
||||
return obj
|
||||
@@ -1,62 +0,0 @@
|
||||
from timeit import timeit
|
||||
from collections import namedtuple
|
||||
from array import array
|
||||
from itertools import izip
|
||||
from collections import deque
|
||||
|
||||
|
||||
class Y(object):
|
||||
UUU = 88
|
||||
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
|
||||
def s(self, x):
|
||||
return self.x + 1
|
||||
|
||||
|
||||
class X(Y):
|
||||
A = 10
|
||||
B = 2
|
||||
C = 4
|
||||
D = 9
|
||||
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
self.stack = []
|
||||
self.par = super(X, self)
|
||||
|
||||
def s(self, x):
|
||||
pass
|
||||
|
||||
def __add__(self, other):
|
||||
return self.x + other.x
|
||||
|
||||
def another(self):
|
||||
return Y.s(self, 1)
|
||||
|
||||
def yet_another(self):
|
||||
return self.par.s(1)
|
||||
|
||||
|
||||
def add(a, b):
|
||||
return a.x + b.x
|
||||
|
||||
|
||||
t = []
|
||||
|
||||
Type = None
|
||||
try:
|
||||
print timeit(
|
||||
"""
|
||||
|
||||
t.append(4)
|
||||
t.pop()
|
||||
|
||||
|
||||
|
||||
""",
|
||||
"from __main__ import X,Y,namedtuple,array,t,add,Type, izip",
|
||||
number=1000000)
|
||||
except:
|
||||
raise
|
||||
@@ -1,28 +0,0 @@
|
||||
def to_key(literal_or_identifier):
|
||||
''' returns string representation of this object'''
|
||||
if literal_or_identifier['type'] == 'Identifier':
|
||||
return literal_or_identifier['name']
|
||||
elif literal_or_identifier['type'] == 'Literal':
|
||||
k = literal_or_identifier['value']
|
||||
if isinstance(k, float):
|
||||
return unicode(float_repr(k))
|
||||
elif 'regex' in literal_or_identifier:
|
||||
return compose_regex(k)
|
||||
elif isinstance(k, bool):
|
||||
return u'true' if k else u'false'
|
||||
elif k is None:
|
||||
return u'null'
|
||||
else:
|
||||
return unicode(k)
|
||||
|
||||
|
||||
def compose_regex(val):
|
||||
reg, flags = val
|
||||
# reg = REGEXP_CONVERTER._unescape_string(reg)
|
||||
return u'/%s/%s' % (reg, flags)
|
||||
|
||||
|
||||
def float_repr(f):
|
||||
if int(f) == f:
|
||||
return unicode(repr(int(f)))
|
||||
return unicode(repr(f))
|
||||
@@ -1 +0,0 @@
|
||||
__author__ = 'Piotrek'
|
||||
@@ -1,308 +0,0 @@
|
||||
from string import ascii_lowercase, digits
|
||||
##################################
|
||||
StringName = u'PyJsConstantString%d_'
|
||||
NumberName = u'PyJsConstantNumber%d_'
|
||||
RegExpName = u'PyJsConstantRegExp%d_'
|
||||
##################################
|
||||
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({'.'})
|
||||
|
||||
|
||||
def _is_cancelled(source, n):
|
||||
cancelled = False
|
||||
k = 0
|
||||
while True:
|
||||
k += 1
|
||||
if source[n - k] != '\\':
|
||||
break
|
||||
cancelled = not cancelled
|
||||
return cancelled
|
||||
|
||||
|
||||
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:
|
||||
return True
|
||||
char = source[n - k]
|
||||
if char in markers:
|
||||
return True
|
||||
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 < len(source) and source[start] in charset:
|
||||
start += 1
|
||||
return start
|
||||
|
||||
|
||||
def parse_exponent(source, start):
|
||||
"""returns end of exponential, raises SyntaxError if failed"""
|
||||
if not source[start] in {'e', 'E'}:
|
||||
if source[start] in IDENTIFIER_PART:
|
||||
raise SyntaxError('Invalid number literal!')
|
||||
return start
|
||||
start += 1
|
||||
if source[start] in {'-', '+'}:
|
||||
start += 1
|
||||
FOUND = False
|
||||
# we need at least one dig after exponent
|
||||
while source[start] in NUMS:
|
||||
FOUND = True
|
||||
start += 1
|
||||
if not FOUND or source[start] in IDENTIFIER_PART:
|
||||
raise SyntaxError('Invalid number literal!')
|
||||
return start
|
||||
|
||||
|
||||
def remove_constants(source):
|
||||
'''Replaces Strings and Regexp literals in the source code with
|
||||
identifiers and *removes comments*. Identifier is of the format:
|
||||
|
||||
PyJsStringConst(String const number)_ - for Strings
|
||||
PyJsRegExpConst(RegExp const number)_ - for RegExps
|
||||
|
||||
Returns dict which relates identifier and replaced constant.
|
||||
|
||||
Removes single line and multiline comments from JavaScript source code
|
||||
Pseudo comments (inside strings) will not be removed.
|
||||
|
||||
For example this line:
|
||||
var x = "/*PSEUDO COMMENT*/ TEXT //ANOTHER PSEUDO COMMENT"
|
||||
will be unaltered'''
|
||||
source = ' ' + source + '\n'
|
||||
comments = []
|
||||
inside_comment, single_comment = False, False
|
||||
inside_single, inside_double = False, False
|
||||
inside_regexp = False
|
||||
regexp_class_count = 0
|
||||
n = 0
|
||||
while n < len(source):
|
||||
char = source[n]
|
||||
if char == '"' and not (inside_comment or inside_single
|
||||
or inside_regexp):
|
||||
if not _is_cancelled(source, n):
|
||||
if inside_double:
|
||||
inside_double[1] = n + 1
|
||||
comments.append(inside_double)
|
||||
inside_double = False
|
||||
else:
|
||||
inside_double = [n, None, 0]
|
||||
elif char == "'" and not (inside_comment or inside_double
|
||||
or inside_regexp):
|
||||
if not _is_cancelled(source, n):
|
||||
if inside_single:
|
||||
inside_single[1] = n + 1
|
||||
comments.append(inside_single)
|
||||
inside_single = False
|
||||
else:
|
||||
inside_single = [n, None, 0]
|
||||
elif (inside_single or inside_double):
|
||||
if char in LINE_TERMINATOR:
|
||||
if _is_cancelled(source, n):
|
||||
if char == CR and source[n + 1] == LF:
|
||||
n += 1
|
||||
n += 1
|
||||
continue
|
||||
else:
|
||||
raise SyntaxError(
|
||||
'Invalid string literal. Line terminators must be escaped!'
|
||||
)
|
||||
else:
|
||||
if inside_comment:
|
||||
if single_comment:
|
||||
if char in LINE_TERMINATOR:
|
||||
inside_comment[1] = n
|
||||
comments.append(inside_comment)
|
||||
inside_comment = False
|
||||
single_comment = False
|
||||
else: # Multiline
|
||||
if char == '/' and source[n - 1] == '*':
|
||||
inside_comment[1] = n + 1
|
||||
comments.append(inside_comment)
|
||||
inside_comment = False
|
||||
elif inside_regexp:
|
||||
if not quiting_regexp:
|
||||
if char in LINE_TERMINATOR:
|
||||
raise SyntaxError(
|
||||
'Invalid regexp literal. Line terminators cant appear!'
|
||||
)
|
||||
if _is_cancelled(source, n):
|
||||
n += 1
|
||||
continue
|
||||
if char == '[':
|
||||
regexp_class_count += 1
|
||||
elif char == ']':
|
||||
regexp_class_count = max(regexp_class_count - 1, 0)
|
||||
elif char == '/' and not regexp_class_count:
|
||||
quiting_regexp = True
|
||||
else:
|
||||
if char not in IDENTIFIER_START:
|
||||
inside_regexp[1] = n
|
||||
comments.append(inside_regexp)
|
||||
inside_regexp = False
|
||||
elif char == '/' and source[n - 1] == '/':
|
||||
single_comment = True
|
||||
inside_comment = [n - 1, None, 1]
|
||||
elif char == '*' and source[n - 1] == '/':
|
||||
inside_comment = [n - 1, None, 1]
|
||||
elif char == '/' and source[n + 1] not in ('/', '*'):
|
||||
if not _ensure_regexp(source, n): #<- improve this one
|
||||
n += 1
|
||||
continue #Probably just a division
|
||||
quiting_regexp = False
|
||||
inside_regexp = [n, None, 2]
|
||||
elif not (inside_comment or inside_regexp):
|
||||
if (char in NUMS and
|
||||
source[n - 1] not in IDENTIFIER_PART) or char == '.':
|
||||
if char == '.':
|
||||
k = parse_num(source, n + 1, NUMS)
|
||||
if k == n + 1: # just a stupid dot...
|
||||
n += 1
|
||||
continue
|
||||
k = parse_exponent(source, k)
|
||||
elif char == '0' and source[n + 1] in {
|
||||
'x', 'X'
|
||||
}: #Hex number probably
|
||||
k = parse_num(source, n + 2, HEX)
|
||||
if k == n + 2 or source[k] in IDENTIFIER_PART:
|
||||
raise SyntaxError('Invalid hex literal!')
|
||||
else: #int or exp or flot or exp flot
|
||||
k = parse_num(source, n + 1, NUMS)
|
||||
if source[k] == '.':
|
||||
k = parse_num(source, k + 1, NUMS)
|
||||
k = parse_exponent(source, k)
|
||||
comments.append((n, k, 3))
|
||||
n = k
|
||||
continue
|
||||
n += 1
|
||||
res = ''
|
||||
start = 0
|
||||
count = 0
|
||||
constants = {}
|
||||
for end, next_start, typ in comments:
|
||||
res += source[start:end]
|
||||
start = next_start
|
||||
if typ == 0: # String
|
||||
name = StringName
|
||||
elif typ == 1: # comment
|
||||
continue
|
||||
elif typ == 2: # regexp
|
||||
name = RegExpName
|
||||
elif typ == 3: # number
|
||||
name = NumberName
|
||||
else:
|
||||
raise RuntimeError()
|
||||
res += ' ' + name % count + ' '
|
||||
constants[name % count] = source[end:next_start]
|
||||
count += 1
|
||||
res += source[start:]
|
||||
# remove this stupid white space
|
||||
for e in WHITE:
|
||||
res = res.replace(e, ' ')
|
||||
res = res.replace(CR + LF, '\n')
|
||||
for e in LINE_TERMINATOR:
|
||||
res = res.replace(e, '\n')
|
||||
return res.strip(), constants
|
||||
|
||||
|
||||
def recover_constants(py_source,
|
||||
replacements): #now has n^2 complexity. improve to n
|
||||
'''Converts identifiers representing Js constants to the PyJs constants
|
||||
PyJsNumberConst_1_ which has the true value of 5 will be converted to PyJsNumber(5)'''
|
||||
for identifier, value in replacements.iteritems():
|
||||
if identifier.startswith('PyJsConstantRegExp'):
|
||||
py_source = py_source.replace(identifier,
|
||||
'JsRegExp(%s)' % repr(value))
|
||||
elif identifier.startswith('PyJsConstantString'):
|
||||
py_source = py_source.replace(
|
||||
identifier, 'Js(u%s)' % unify_string_literals(value))
|
||||
else:
|
||||
py_source = py_source.replace(identifier, 'Js(%s)' % value)
|
||||
return py_source
|
||||
|
||||
|
||||
def unify_string_literals(js_string):
|
||||
"""this function parses the string just like javascript
|
||||
for example literal '\d' in JavaScript would be interpreted
|
||||
as 'd' - backslash would be ignored and in Pyhon this
|
||||
would be interpreted as '\\d' This function fixes this problem."""
|
||||
n = 0
|
||||
res = ''
|
||||
limit = len(js_string)
|
||||
while n < limit:
|
||||
char = js_string[n]
|
||||
if char == '\\':
|
||||
new, n = do_escape(js_string, n)
|
||||
res += new
|
||||
else:
|
||||
res += char
|
||||
n += 1
|
||||
return res
|
||||
|
||||
|
||||
def unify_regexp_literals(js):
|
||||
pass
|
||||
|
||||
|
||||
def do_escape(source, n):
|
||||
"""Its actually quite complicated to cover every case :)
|
||||
http://www.javascriptkit.com/jsref/escapesequence.shtml"""
|
||||
if not n + 1 < len(source):
|
||||
return '' # not possible here but can be possible in general case.
|
||||
if source[n + 1] in LINE_TERMINATOR:
|
||||
if source[n + 1] == CR and n + 2 < len(source) and source[n + 2] == LF:
|
||||
return source[n:n + 3], n + 3
|
||||
return source[n:n + 2], n + 2
|
||||
if source[n + 1] in ESCAPE_CHARS:
|
||||
return source[n:n + 2], n + 2
|
||||
if source[n + 1] in {'x', 'u'}:
|
||||
char, length = ('u', 4) if source[n + 1] == 'u' else ('x', 2)
|
||||
n += 2
|
||||
end = parse_num(source, n, HEX)
|
||||
if end - n < length:
|
||||
raise SyntaxError('Invalid escape sequence!')
|
||||
#if length==4:
|
||||
# return unichr(int(source[n:n+4], 16)), n+4 # <- this was a very bad way of solving this problem :)
|
||||
return source[n - 2:n + length], n + length
|
||||
if source[n + 1] in OCTAL:
|
||||
n += 1
|
||||
end = parse_num(source, n, OCTAL)
|
||||
end = min(end, n + 3) # cant be longer than 3
|
||||
# now the max allowed is 377 ( in octal) and 255 in decimal
|
||||
max_num = 255
|
||||
num = 0
|
||||
len_parsed = 0
|
||||
for e in source[n:end]:
|
||||
cand = 8 * num + int(e)
|
||||
if cand > max_num:
|
||||
break
|
||||
num = cand
|
||||
len_parsed += 1
|
||||
# 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
|
||||
|
||||
|
||||
#####TEST######
|
||||
|
||||
if __name__ == '__main__':
|
||||
test = ('''
|
||||
''')
|
||||
|
||||
t, d = remove_constants(test)
|
||||
print t, d
|
||||
@@ -1,83 +0,0 @@
|
||||
"""
|
||||
exp_translate routine:
|
||||
It takes a single line of JS code and returns a SINGLE line of Python code.
|
||||
Note var is not present here because it was removed in previous stages. Also remove this useless void keyword
|
||||
If case of parsing errors it must return a pos of error.
|
||||
1. Convert all assignment operations to put operations, this may be hard :( DONE, wasn't that bad
|
||||
2. Convert all gets and calls to get and callprop.
|
||||
3. Convert unary operators like typeof, new, !, delete, ++, --
|
||||
Delete can be handled by replacing last get method with delete.
|
||||
4. Convert remaining operators that are not handled by python:
|
||||
&&, || <= these should be easy simply replace && by and and || by or
|
||||
=== and !==
|
||||
comma operator , in, instanceof and finally :?
|
||||
|
||||
|
||||
NOTES:
|
||||
Strings and other literals are not present so each = means assignment
|
||||
"""
|
||||
from utils import *
|
||||
from jsparser import *
|
||||
|
||||
|
||||
def exps_translator(js):
|
||||
#Check () {} and [] nums
|
||||
ass = assignment_translator(js)
|
||||
|
||||
|
||||
# Step 1
|
||||
def assignment_translator(js):
|
||||
sep = js.split(',')
|
||||
res = sep[:]
|
||||
for i, e in enumerate(sep):
|
||||
if '=' not in e: # no need to convert
|
||||
continue
|
||||
res[i] = bass_translator(e)
|
||||
return ','.join(res)
|
||||
|
||||
|
||||
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]) + ']'
|
||||
else:
|
||||
converted += e
|
||||
s = converted
|
||||
if '=' not in s:
|
||||
return s
|
||||
ass = reversed(s.split('='))
|
||||
last = ass.next()
|
||||
res = last
|
||||
for e in ass:
|
||||
op = ''
|
||||
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
|
||||
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 []
|
||||
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)
|
||||
else: # Prop set via '.'
|
||||
c = cand.rfind('.')
|
||||
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)
|
||||
return res
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print bass_translator('3.ddsd = 40')
|
||||
@@ -1,480 +0,0 @@
|
||||
"""This module translates JS flow into PY flow.
|
||||
|
||||
Translates:
|
||||
IF ELSE
|
||||
|
||||
DO WHILE
|
||||
WHILE
|
||||
FOR 123
|
||||
FOR iter
|
||||
CONTINUE, BREAK, RETURN, LABEL, THROW, TRY, SWITCH
|
||||
"""
|
||||
from utils import *
|
||||
from jsparser import *
|
||||
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')
|
||||
|
||||
|
||||
def get_break_label(label):
|
||||
return BREAK_LABEL % label.encode('hex')
|
||||
|
||||
|
||||
def pass_until(source, start, tokens=(';', )):
|
||||
while start < len(source) and source[start] not in tokens:
|
||||
start += 1
|
||||
return start + 1
|
||||
|
||||
|
||||
def do_bracket_exp(source, start, throw=True):
|
||||
bra, cand = pass_bracket(source, start, '()')
|
||||
if throw and not bra:
|
||||
raise SyntaxError('Missing bracket expression')
|
||||
bra = exp_translator(bra[1:-1])
|
||||
if throw and not bra:
|
||||
raise SyntaxError('Empty bracket condition')
|
||||
return bra, cand if bra else start
|
||||
|
||||
|
||||
def do_if(source, start):
|
||||
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)
|
||||
|
||||
elseif = except_keyword(source, start, 'else')
|
||||
is_elseif = False
|
||||
if elseif:
|
||||
start = elseif
|
||||
if except_keyword(source, start, 'if'):
|
||||
is_elseif = True
|
||||
elseif, start = do_statement(source, start)
|
||||
if elseif is None:
|
||||
raise SyntaxError('Invalid if statement)')
|
||||
if is_elseif:
|
||||
translated += 'el' + elseif
|
||||
else:
|
||||
translated += 'else:\n' + indent(elseif)
|
||||
return translated, start
|
||||
|
||||
|
||||
def do_statement(source, start):
|
||||
"""returns none if not found other functions that begin with 'do_' raise
|
||||
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
|
||||
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
|
||||
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:
|
||||
return meth(source, start)
|
||||
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
|
||||
label, cand_start = cand
|
||||
cand_start = pass_white(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
|
||||
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
|
||||
|
||||
|
||||
def do_dowhile(source, start):
|
||||
start += 2 # pass do
|
||||
statement, start = do_statement(source, start)
|
||||
if statement is None:
|
||||
raise SyntaxError('Missing statement to execute in do while loop!')
|
||||
start = except_keyword(source, start, 'while')
|
||||
if not 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
|
||||
|
||||
|
||||
def do_block(source, start):
|
||||
bra, start = pass_bracket(source, start, '{}')
|
||||
#print source[start:], bra
|
||||
#return bra +'\n', start
|
||||
if bra is None:
|
||||
raise SyntaxError('Missing block ( {code} )')
|
||||
code = ''
|
||||
bra = bra[1:-1] + ';'
|
||||
bra_pos = 0
|
||||
while bra_pos < len(bra):
|
||||
st, bra_pos = do_statement(bra, bra_pos)
|
||||
if st is None:
|
||||
break
|
||||
code += st
|
||||
bra_pos = pass_white(bra, bra_pos)
|
||||
if bra_pos < len(bra):
|
||||
raise SyntaxError('Block has more code that could not be parsed:\n' +
|
||||
bra[bra_pos:])
|
||||
return code, start
|
||||
|
||||
|
||||
def do_empty(source, start):
|
||||
return 'pass\n', start + 1
|
||||
|
||||
|
||||
def do_expression(source, start):
|
||||
start = pass_white(source, start)
|
||||
end = pass_until(source, start, tokens=(';', ))
|
||||
if end == start + 1: #empty statement
|
||||
return 'pass\n', end
|
||||
# AUTOMATIC SEMICOLON INSERTION FOLLOWS
|
||||
# Without ASI this function would end with: return exp_translator(source[start:end].rstrip(';'))+'\n', end
|
||||
# ASI makes things a bit more complicated:
|
||||
# we will try to parse as much as possible, inserting ; in place of last new line in case of error
|
||||
rev = False
|
||||
rpos = 0
|
||||
while True:
|
||||
try:
|
||||
code = source[start:end].rstrip(';')
|
||||
cand = exp_translator(code) + '\n', end
|
||||
just_to_test = compile(cand[0], '', 'exec')
|
||||
return cand
|
||||
except Exception as e:
|
||||
if not rev:
|
||||
rev = source[start:end][::-1]
|
||||
lpos = rpos
|
||||
while True:
|
||||
rpos = pass_until(rev, rpos, LINE_TERMINATOR)
|
||||
if rpos >= len(rev):
|
||||
raise
|
||||
if filter(lambda x: x not in SPACE, rev[lpos:rpos]):
|
||||
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
|
||||
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 < len(
|
||||
de
|
||||
): # we have something more to parse... It has to start with =
|
||||
if de[var_end] != '=':
|
||||
raise SyntaxError(
|
||||
'Unexpected initializer in var statement. Expected "=", got "%s"'
|
||||
% de[var_end])
|
||||
code += exp_translator(de) + '\n'
|
||||
if not code.strip():
|
||||
code = 'pass\n'
|
||||
return code, end
|
||||
|
||||
|
||||
def do_label(source, start):
|
||||
label, end = parse_identifier(source, start)
|
||||
end = pass_white(source, end)
|
||||
#now source[end] must be :
|
||||
assert source[end] == ':'
|
||||
end += 1
|
||||
inside, end = do_statement(source, end)
|
||||
if inside is None:
|
||||
raise SyntaxError('Missing statement after label')
|
||||
defs = ''
|
||||
if inside.startswith('while ') or inside.startswith(
|
||||
'for ') or inside.startswith('#for'):
|
||||
# we have to add contine label as well...
|
||||
# 3 or 1 since #for loop type has more lines before real for.
|
||||
sep = 1 if not inside.startswith('#for') else 3
|
||||
cont_label = get_continue_label(label)
|
||||
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)
|
||||
inside = 'try:\n%sexcept %s:\n pass\n' % (indent(inside), break_label)
|
||||
defs += 'class %s(Exception): pass\n' % break_label
|
||||
return defs + inside, end
|
||||
|
||||
|
||||
def do_for(source, start):
|
||||
start += 3 # pass for
|
||||
entered = start
|
||||
bra, start = pass_bracket(source, start, '()')
|
||||
inside, start = do_statement(source, start)
|
||||
if inside is None:
|
||||
raise SyntaxError('Missing statement after for')
|
||||
bra = bra[1:-1]
|
||||
if ';' in bra:
|
||||
init = argsplit(bra, ';')
|
||||
if len(init) != 3:
|
||||
raise SyntaxError('Invalid for statement')
|
||||
args = []
|
||||
for i, item in enumerate(init):
|
||||
end = pass_white(item, 0)
|
||||
if end == len(item):
|
||||
args.append('' if i != 1 else '1')
|
||||
continue
|
||||
if not i and except_keyword(item, end, 'var') is not None:
|
||||
# var statement
|
||||
args.append(do_var(item, end)[0])
|
||||
continue
|
||||
args.append(do_expression(item, end)[0])
|
||||
return '#for JS loop\n%swhile %s:\n%s%s\n' % (
|
||||
args[0], args[1].strip(), indent(inside), indent(args[2])), start
|
||||
# iteration
|
||||
end = pass_white(bra, 0)
|
||||
register = False
|
||||
if bra[end:].startswith('var '):
|
||||
end += 3
|
||||
end = pass_white(bra, end)
|
||||
register = True
|
||||
name, end = parse_identifier(bra, end)
|
||||
if register:
|
||||
TO_REGISTER.append(name)
|
||||
end = pass_white(bra, end)
|
||||
if bra[end:end + 2] != 'in' or bra[end + 2] in IDENTIFIER_PART:
|
||||
#print source[entered-10:entered+50]
|
||||
raise SyntaxError('Invalid "for x in y" statement')
|
||||
end += 2 # pass in
|
||||
exp = exp_translator(bra[end:])
|
||||
res = 'for temp in %s:\n' % exp
|
||||
res += indent('var.put(%s, temp)\n' % name.__repr__()) + indent(inside)
|
||||
return res, start
|
||||
|
||||
|
||||
# todo - IMPORTANT
|
||||
def do_continue(source, start, name='continue'):
|
||||
start += len(name) #pass continue
|
||||
start = pass_white(source, start)
|
||||
if start < len(source) and source[start] == ';':
|
||||
return '%s\n' % name, start + 1
|
||||
# labeled statement or error
|
||||
label, start = parse_identifier(source, start)
|
||||
start = pass_white(source, start)
|
||||
if start < len(source) and source[start] != ';':
|
||||
raise SyntaxError('Missing ; after label name in %s statement' % name)
|
||||
return 'raise %s("%s")\n' % (get_continue_label(label)
|
||||
if name == 'continue' else
|
||||
get_break_label(label), name), start + 1
|
||||
|
||||
|
||||
def do_break(source, start):
|
||||
return do_continue(source, start, 'break')
|
||||
|
||||
|
||||
def do_return(source, start):
|
||||
start += 6 # pass return
|
||||
end = source.find(';', start) + 1
|
||||
if end == -1:
|
||||
end = len(source)
|
||||
trans = exp_translator(source[start:end].rstrip(';'))
|
||||
return 'return %s\n' % (trans if trans else "var.get('undefined')"), end
|
||||
|
||||
|
||||
# todo later?- Also important
|
||||
def do_throw(source, start):
|
||||
start += 5 # pass throw
|
||||
end = source.find(';', start) + 1
|
||||
if not end:
|
||||
end = len(source)
|
||||
trans = exp_translator(source[start:end].rstrip(';'))
|
||||
if not trans:
|
||||
raise SyntaxError('Invalid throw statement: nothing to throw')
|
||||
res = 'PyJsTempException = JsToPyException(%s)\nraise PyJsTempException\n' % trans
|
||||
return res, end
|
||||
|
||||
|
||||
def do_try(source, start):
|
||||
start += 3 # pass try
|
||||
block, start = do_block(source, start)
|
||||
result = 'try:\n%s' % indent(block)
|
||||
catch = except_keyword(source, start, 'catch')
|
||||
if catch:
|
||||
bra, catch = pass_bracket(source, catch, '()')
|
||||
bra = bra[1:-1]
|
||||
identifier, bra_end = parse_identifier(bra, 0)
|
||||
holder = 'PyJsHolder_%s_%d' % (identifier.encode('hex'),
|
||||
random.randrange(1e8))
|
||||
identifier = identifier.__repr__()
|
||||
bra_end = pass_white(bra, bra_end)
|
||||
if bra_end < len(bra):
|
||||
raise SyntaxError('Invalid content of catch statement')
|
||||
result += 'except PyJsException as PyJsTempException:\n'
|
||||
block, catch = do_block(source, catch)
|
||||
# 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(block)))
|
||||
start = max(catch, start)
|
||||
final = except_keyword(source, start, 'finally')
|
||||
if not (final or catch):
|
||||
raise SyntaxError(
|
||||
'Try statement has to be followed by catch or finally')
|
||||
if not final:
|
||||
return result, start
|
||||
# translate finally statement
|
||||
block, start = do_block(source, final)
|
||||
return result + 'finally:\n%s' % indent(block), start
|
||||
|
||||
|
||||
def do_debugger(source, start):
|
||||
start += 8 # pass debugger
|
||||
end = pass_white(source, start)
|
||||
if end < len(source) and source[end] == ';':
|
||||
end += 1
|
||||
return 'pass\n', end #ignore errors...
|
||||
|
||||
|
||||
# todo automatic ; insertion. fuck this crappy feature
|
||||
|
||||
# Least important
|
||||
|
||||
|
||||
def do_switch(source, start):
|
||||
start += 6 # pass switch
|
||||
code = 'while 1:\n' + indent('SWITCHED = False\nCONDITION = (%s)\n')
|
||||
# parse value of check
|
||||
val, start = pass_bracket(source, start, '()')
|
||||
if val is None:
|
||||
raise SyntaxError('Missing () after switch statement')
|
||||
if not val.strip():
|
||||
raise SyntaxError('Missing content inside () after switch statement')
|
||||
code = code % exp_translator(val)
|
||||
bra, start = pass_bracket(source, start, '{}')
|
||||
if bra is None:
|
||||
raise SyntaxError('Missing block {} after switch statement')
|
||||
bra_pos = 0
|
||||
bra = bra[1:-1] + ';'
|
||||
while True:
|
||||
case = except_keyword(bra, bra_pos, 'case')
|
||||
default = except_keyword(bra, bra_pos, 'default')
|
||||
assert not (case and default)
|
||||
if case or default: # this ?: expression makes things much harder....
|
||||
case_code = None
|
||||
if case:
|
||||
case_code = 'if SWITCHED or PyJsStrictEq(CONDITION, %s):\n'
|
||||
# we are looking for a first : with count 1. ? gives -1 and : gives +1.
|
||||
count = 0
|
||||
for pos, e in enumerate(bra[case:], case):
|
||||
if e == '?':
|
||||
count -= 1
|
||||
elif e == ':':
|
||||
count += 1
|
||||
if count == 1:
|
||||
break
|
||||
else:
|
||||
raise SyntaxError(
|
||||
'Missing : token after case in switch statement')
|
||||
case_condition = exp_translator(
|
||||
bra[case:pos]) # switch {case CONDITION: statements}
|
||||
case_code = case_code % case_condition
|
||||
case = pos + 1
|
||||
if default:
|
||||
case = except_token(bra, default, ':')
|
||||
case_code = 'if True:\n'
|
||||
# now parse case statements (things after ':' )
|
||||
cand, case = do_statement(bra, case)
|
||||
while cand:
|
||||
case_code += indent(cand)
|
||||
cand, case = do_statement(bra, case)
|
||||
case_code += indent('SWITCHED = True\n')
|
||||
code += indent(case_code)
|
||||
bra_pos = case
|
||||
else:
|
||||
break
|
||||
# prevent infinite loop :)
|
||||
code += indent('break\n')
|
||||
return code, start
|
||||
|
||||
|
||||
def do_pyimport(source, start):
|
||||
start += 8
|
||||
lib, start = parse_identifier(source, start)
|
||||
jlib = 'PyImport_%s' % lib
|
||||
code = 'import %s as %s\n' % (lib, jlib)
|
||||
#check whether valid lib name...
|
||||
try:
|
||||
compile(code, '', 'exec')
|
||||
except:
|
||||
raise SyntaxError(
|
||||
'Invalid Python module name (%s) in pyimport statement' % lib)
|
||||
# var.pyimport will handle module conversion to PyJs object
|
||||
code += 'var.pyimport(%s, %s)\n' % (repr(lib), jlib)
|
||||
return code, start
|
||||
|
||||
|
||||
def do_with(source, start):
|
||||
raise NotImplementedError('With statement is not implemented yet :(')
|
||||
|
||||
|
||||
KEYWORD_METHODS = {
|
||||
'do': do_dowhile,
|
||||
'while': do_while,
|
||||
'if': do_if,
|
||||
'throw': do_throw,
|
||||
'return': do_return,
|
||||
'continue': do_continue,
|
||||
'break': do_break,
|
||||
'try': do_try,
|
||||
'for': do_for,
|
||||
'switch': do_switch,
|
||||
'var': do_var,
|
||||
'debugger': do_debugger, # this one does not do anything
|
||||
'with': do_with,
|
||||
'pyimport': do_pyimport
|
||||
}
|
||||
|
||||
#Also not specific statements (harder to detect)
|
||||
# Block {}
|
||||
# Expression or Empty Statement
|
||||
# Label
|
||||
#
|
||||
# Its easy to recognize block but harder to distinguish between label and expression statement
|
||||
|
||||
|
||||
def translate_flow(source):
|
||||
"""Source cant have arrays, object, constant or function literals.
|
||||
Returns PySource and variables to register"""
|
||||
global TO_REGISTER
|
||||
TO_REGISTER = []
|
||||
return do_block('{%s}' % source, 0)[0], TO_REGISTER
|
||||
|
||||
|
||||
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]
|
||||
@@ -1,98 +0,0 @@
|
||||
"""This module removes JS functions from source code"""
|
||||
from jsparser import *
|
||||
from utils import *
|
||||
|
||||
INLINE_NAME = 'PyJsLvalInline%d_'
|
||||
INLINE_COUNT = 0
|
||||
PRE_EXP_STARTS = {
|
||||
'return', 'new', 'void', 'throw', 'typeof', 'in', 'instanceof'
|
||||
}
|
||||
PRE_ALLOWED = IDENTIFIER_PART.union({';', '{', '}', ']', ')', ':'})
|
||||
INCREMENTS = {'++', '--'}
|
||||
|
||||
|
||||
def reset_inline_count():
|
||||
global INLINE_COUNT
|
||||
INLINE_COUNT = 0
|
||||
|
||||
|
||||
def remove_functions(source, all_inline=False):
|
||||
"""removes functions and returns new source, and 2 dicts.
|
||||
first dict with removed hoisted(global) functions and second with replaced inline functions"""
|
||||
global INLINE_COUNT
|
||||
inline = {}
|
||||
hoisted = {}
|
||||
n = 0
|
||||
limit = len(source) - 9 # 8 is length of 'function'
|
||||
res = ''
|
||||
last = 0
|
||||
while n < limit:
|
||||
if n and source[n - 1] in IDENTIFIER_PART:
|
||||
n += 1
|
||||
continue
|
||||
if source[n:n + 8] == 'function' and source[n +
|
||||
8] not in IDENTIFIER_PART:
|
||||
if source[:n].rstrip().endswith(
|
||||
'.'): # allow function as a property name :)
|
||||
n += 1
|
||||
continue
|
||||
if source[n + 8:].lstrip().startswith(
|
||||
':'): # allow functions inside objects...
|
||||
n += 1
|
||||
continue
|
||||
entered = n
|
||||
res += source[last:n]
|
||||
name = ''
|
||||
n = pass_white(source, n + 8)
|
||||
if source[n] in IDENTIFIER_START: # hoisted function
|
||||
name, n = parse_identifier(source, n)
|
||||
args, n = pass_bracket(source, n, '()')
|
||||
if not args:
|
||||
raise SyntaxError('Function misses bracket with argnames ()')
|
||||
args = args.strip('() \n')
|
||||
args = tuple(parse_identifier(e, 0)[0]
|
||||
for e in argsplit(args)) if args else ()
|
||||
if len(args) - len(set(args)):
|
||||
# I know its legal in JS but python does not allow duplicate argnames
|
||||
# I will not work around it
|
||||
raise SyntaxError(
|
||||
'Function has duplicate argument names. Its not legal in this implementation. Sorry.'
|
||||
)
|
||||
block, n = pass_bracket(source, n, '{}')
|
||||
if not block:
|
||||
raise SyntaxError(
|
||||
'Function does not have any code block to execute')
|
||||
mixed = False # named function expression flag
|
||||
if name and not all_inline:
|
||||
# Here I will distinguish between named function expression (mixed) and a function statement
|
||||
before = source[:entered].rstrip()
|
||||
if any(endswith_keyword(before, e) for e in PRE_EXP_STARTS):
|
||||
#print 'Ended ith keyword'
|
||||
mixed = True
|
||||
elif before and before[-1] not in PRE_ALLOWED and not before[
|
||||
-2:] in INCREMENTS:
|
||||
#print 'Ended with'+repr(before[-1]), before[-1]=='}'
|
||||
mixed = True
|
||||
else:
|
||||
#print 'FUNCTION STATEMENT'
|
||||
#its a function statement.
|
||||
# todo remove fucking label if present!
|
||||
hoisted[name] = block, args
|
||||
if not name or mixed or all_inline: # its a function expression (can be both named and not named)
|
||||
#print 'FUNCTION EXPRESSION'
|
||||
INLINE_COUNT += 1
|
||||
iname = INLINE_NAME % INLINE_COUNT # inline name
|
||||
res += ' ' + iname
|
||||
inline['%s@%s' % (
|
||||
iname, name
|
||||
)] = block, args #here added real name at the end because it has to be added to the func scope
|
||||
last = n
|
||||
else:
|
||||
n += 1
|
||||
res += source[last:]
|
||||
return res, hoisted, inline
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print remove_functions(
|
||||
'5+5 function n (functiona ,functionaj) {dsd s, dsdd}')
|
||||
@@ -1,326 +0,0 @@
|
||||
"""
|
||||
The process of translating JS will go like that: # TOP = 'imports and scope set'
|
||||
|
||||
1. Remove all the comments
|
||||
2. Replace number, string and regexp literals with markers
|
||||
4. Remove global Functions and move their translation to the TOP. Also add register code there.
|
||||
5. Replace inline functions with lvals
|
||||
6. Remove List and Object literals and replace them with lvals
|
||||
7. Find and remove var declarations, generate python register code that would go on TOP.
|
||||
|
||||
Here we should be left with global code only where 1 line of js code = 1 line of python code.
|
||||
Routine translating this code should be called glob_translate:
|
||||
1. Search for outer structures and translate them using glob and inside using exps_translate
|
||||
|
||||
|
||||
exps_translate routine:
|
||||
1. Remove outer {}
|
||||
2. Split lines at ;
|
||||
3. Convert line by line using exp_translate
|
||||
4. In case of error in 3 try to insert ; according to ECMA rules and repeat 3.
|
||||
|
||||
exp_translate routine:
|
||||
It takes a single line of JS code and returns a SINGLE line of Python code.
|
||||
Note var is not present here because it was removed in previous stages.
|
||||
If case of parsing errors it must return a pos of error.
|
||||
1. Convert all assignment operations to put operations, this may be hard :(
|
||||
2. Convert all gets and calls to get and callprop.
|
||||
3. Convert unary operators like typeof, new, !, delete.
|
||||
Delete can be handled by replacing last get method with delete.
|
||||
4. Convert remaining operators that are not handled by python eg: === and ,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
lval format PyJsLvalNR
|
||||
marker PyJs(TYPE_NAME)(NR)
|
||||
|
||||
TODO
|
||||
1. Number literal replacement
|
||||
2. Array literal replacement
|
||||
3. Object literal replacement
|
||||
5. Function replacement
|
||||
4. Literal replacement translators
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from utils import *
|
||||
|
||||
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:
|
||||
f.write(source)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def indent(lines, ind=4):
|
||||
return ind * ' ' + lines.replace('\n', '\n' + ind * ' ').rstrip(' ')
|
||||
|
||||
|
||||
def inject_before_lval(source, lval, code):
|
||||
if source.count(lval) > 1:
|
||||
dbg(source)
|
||||
print
|
||||
print lval
|
||||
raise RuntimeError('To many lvals (%s)' % lval)
|
||||
elif not source.count(lval):
|
||||
dbg(source)
|
||||
print
|
||||
print lval
|
||||
assert lval not in source
|
||||
raise RuntimeError('No lval found "%s"' % lval)
|
||||
end = source.index(lval)
|
||||
inj = source.rfind('\n', 0, end)
|
||||
ind = inj
|
||||
while source[ind + 1] == ' ':
|
||||
ind += 1
|
||||
ind -= inj
|
||||
return source[:inj + 1] + indent(code, ind) + source[inj + 1:]
|
||||
|
||||
|
||||
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):
|
||||
e = source[n]
|
||||
if not in_bracket and e in starts:
|
||||
in_bracket = 1
|
||||
start = n
|
||||
b_start, b_end = brackets[starts.index(e)]
|
||||
elif in_bracket:
|
||||
if e == b_start:
|
||||
in_bracket += 1
|
||||
elif e == b_end:
|
||||
in_bracket -= 1
|
||||
if not in_bracket:
|
||||
if source[last:start]:
|
||||
yield source[last:start]
|
||||
last = n + 1
|
||||
yield source[start + strip:n + 1 - strip]
|
||||
n += 1
|
||||
if source[last:]:
|
||||
yield source[last:]
|
||||
|
||||
|
||||
def pass_bracket(source, start, bracket='()'):
|
||||
"""Returns content of brackets with brackets and first pos after brackets
|
||||
if source[start] is followed by some optional white space and brackets.
|
||||
Otherwise None"""
|
||||
e = bracket_split(source[start:], [bracket], False)
|
||||
try:
|
||||
cand = e.next()
|
||||
except StopIteration:
|
||||
return None, None
|
||||
if not cand.strip(): #white space...
|
||||
try:
|
||||
res = e.next()
|
||||
return res, start + len(cand) + len(res)
|
||||
except StopIteration:
|
||||
return None, None
|
||||
elif cand[-1] == bracket[1]:
|
||||
return cand, start + len(cand)
|
||||
else:
|
||||
return None, None
|
||||
|
||||
|
||||
def startswith_keyword(start, keyword):
|
||||
start = start.lstrip()
|
||||
if start.startswith(keyword):
|
||||
if len(keyword) < len(start):
|
||||
if start[len(keyword)] in IDENTIFIER_PART:
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def endswith_keyword(ending, keyword):
|
||||
ending = ending.rstrip()
|
||||
if ending.endswith(keyword):
|
||||
if len(keyword) < len(ending):
|
||||
if ending[len(ending) - len(keyword) - 1] in IDENTIFIER_PART:
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def pass_white(source, start):
|
||||
n = start
|
||||
while n < len(source):
|
||||
if source[n] in SPACE:
|
||||
n += 1
|
||||
else:
|
||||
break
|
||||
return n
|
||||
|
||||
|
||||
def except_token(source, start, token, throw=True):
|
||||
"""Token can be only a single char. Returns position after token if found. Otherwise raises syntax error if throw
|
||||
otherwise returns None"""
|
||||
start = pass_white(source, start)
|
||||
if start < len(source) and source[start] == token:
|
||||
return start + 1
|
||||
if throw:
|
||||
raise SyntaxError('Missing token. Expected %s' % token)
|
||||
return None
|
||||
|
||||
|
||||
def except_keyword(source, start, keyword):
|
||||
""" Returns position after keyword if found else None
|
||||
Note: skips white space"""
|
||||
start = pass_white(source, start)
|
||||
kl = len(keyword) #keyword len
|
||||
if kl + start > len(source):
|
||||
return None
|
||||
if source[start:start + kl] != keyword:
|
||||
return None
|
||||
if kl + start < len(source) and source[start + kl] in IDENTIFIER_PART:
|
||||
return None
|
||||
return start + kl
|
||||
|
||||
|
||||
def parse_identifier(source, start, throw=True):
|
||||
"""passes white space from start and returns first identifier,
|
||||
if identifier invalid and throw raises SyntaxError otherwise returns None"""
|
||||
start = pass_white(source, start)
|
||||
end = start
|
||||
if not end < len(source):
|
||||
if throw:
|
||||
raise SyntaxError('Missing identifier!')
|
||||
return None
|
||||
if source[end] not in IDENTIFIER_START:
|
||||
if throw:
|
||||
raise SyntaxError('Invalid identifier start: "%s"' % source[end])
|
||||
return None
|
||||
end += 1
|
||||
while end < len(source) and source[end] in IDENTIFIER_PART:
|
||||
end += 1
|
||||
if not is_valid_lval(source[start:end]):
|
||||
if throw:
|
||||
raise SyntaxError(
|
||||
'Invalid identifier name: "%s"' % source[start:end])
|
||||
return None
|
||||
return source[start:end], end
|
||||
|
||||
|
||||
def argsplit(args, sep=','):
|
||||
"""used to split JS args (it is not that simple as it seems because
|
||||
sep can be inside brackets).
|
||||
|
||||
pass args *without* brackets!
|
||||
|
||||
Used also to parse array and object elements, and more"""
|
||||
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])
|
||||
last = parsed_len + i + 1
|
||||
parsed_len += len(e)
|
||||
splits.append(args[last:])
|
||||
return splits
|
||||
|
||||
|
||||
def split_add_ops(text):
|
||||
"""Specialized function splitting text at add/sub operators.
|
||||
Operands are *not* translated. Example result ['op1', '+', 'op2', '-', 'op3']"""
|
||||
n = 0
|
||||
text = text.replace('++', '##').replace(
|
||||
'--', '@@') #text does not normally contain any of these
|
||||
spotted = False # set to true if noticed anything other than +- or white space
|
||||
last = 0
|
||||
while n < len(text):
|
||||
e = text[n]
|
||||
if e == '+' or e == '-':
|
||||
if spotted:
|
||||
yield text[last:n].replace('##', '++').replace('@@', '--')
|
||||
yield e
|
||||
last = n + 1
|
||||
spotted = False
|
||||
elif e == '/' or e == '*' or e == '%':
|
||||
spotted = False
|
||||
elif e != ' ':
|
||||
spotted = True
|
||||
n += 1
|
||||
yield text[last:n].replace('##', '++').replace('@@', '--')
|
||||
|
||||
|
||||
def split_at_any(text,
|
||||
lis,
|
||||
translate=False,
|
||||
not_before=[],
|
||||
not_after=[],
|
||||
validitate=None):
|
||||
""" doc """
|
||||
lis.sort(key=lambda x: len(x), reverse=True)
|
||||
last = 0
|
||||
n = 0
|
||||
text_len = len(text)
|
||||
while n < text_len:
|
||||
if any(text[:n].endswith(e)
|
||||
for e in not_before): #Cant end with end before
|
||||
n += 1
|
||||
continue
|
||||
for e in lis:
|
||||
s = len(e)
|
||||
if s + n > text_len:
|
||||
continue
|
||||
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
|
||||
break
|
||||
if e == text[n:n + s]:
|
||||
yield text[last:n] if not translate else translate(
|
||||
text[last:n])
|
||||
yield e
|
||||
n += s
|
||||
last = n
|
||||
break
|
||||
else:
|
||||
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)
|
||||
last = 0
|
||||
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):
|
||||
pass
|
||||
else:
|
||||
yield text[last:n]
|
||||
last = n + s
|
||||
n += s - 1
|
||||
n += 1
|
||||
yield text[last:]
|
||||
@@ -1,562 +0,0 @@
|
||||
from jsparser import *
|
||||
from utils import *
|
||||
import re
|
||||
from utils import *
|
||||
|
||||
#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...
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def comb_validitator(keyword, before, after):
|
||||
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
|
||||
REPL[name] = e
|
||||
elif e[0] == '(': # can be a function call
|
||||
name = '@PYJSREPL' + str(len(REPL)) + '}'
|
||||
new += name
|
||||
REPL[name] = e
|
||||
else:
|
||||
new += e
|
||||
return new
|
||||
|
||||
|
||||
class NodeVisitor:
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
|
||||
def rl(self, lis, op):
|
||||
"""performs this operation on a list from *right to left*
|
||||
op must take 2 args
|
||||
a,b,c => op(a, op(b, c))"""
|
||||
it = reversed(lis)
|
||||
res = trans(it.next())
|
||||
for e in it:
|
||||
e = trans(e)
|
||||
res = op(e, res)
|
||||
return res
|
||||
|
||||
def lr(self, lis, op):
|
||||
"""performs this operation on a list from *left to right*
|
||||
op must take 2 args
|
||||
a,b,c => op(op(a, b), c)"""
|
||||
it = iter(lis)
|
||||
res = trans(it.next())
|
||||
for e in it:
|
||||
e = trans(e)
|
||||
res = op(res, e)
|
||||
return res
|
||||
|
||||
def translate(self):
|
||||
"""Translates outer operation and calls translate on inner operation.
|
||||
Returns fully translated code."""
|
||||
if not self.code:
|
||||
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
|
||||
return self.lr(cand, js_comma)
|
||||
#Check = operator:
|
||||
# dont split at != or !== or == or === or <= or >=
|
||||
#note <<=, >>= or this >>> will NOT be supported
|
||||
# maybe I will change my mind later
|
||||
# Find this crappy ?:
|
||||
if '?' in new:
|
||||
cond_ind = new.find('?')
|
||||
tenary_start = 0
|
||||
for ass in re.finditer(ASSIGNMENT_MATCH, new):
|
||||
cand = ass.span()[1]
|
||||
if cand < cond_ind:
|
||||
tenary_start = cand
|
||||
else:
|
||||
break
|
||||
actual_tenary = new[tenary_start:]
|
||||
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
|
||||
it = reversed(cand)
|
||||
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__()
|
||||
e = e[:-2]
|
||||
elif e[-1:] in OP_METHODS:
|
||||
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':
|
||||
raise SyntaxError('Invalid left-hand side in assignment')
|
||||
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
|
||||
order = [OR, AND, BOR, BXOR, BAND, EQS, COMPS, BSHIFTS, ADDS, MULTS]
|
||||
# actually we dont need OR and AND because they can be handled easier. But just for fun
|
||||
dangerous = ['<', '>']
|
||||
for typ in order:
|
||||
#we have to use special method for ADDS since they can be also unary operation +/++ or -/-- FUCK
|
||||
if '+' in typ:
|
||||
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:
|
||||
continue
|
||||
n = 1
|
||||
res = trans(cand[0])
|
||||
if not res:
|
||||
raise SyntaxError("Missing operand!")
|
||||
while n < len(cand):
|
||||
e = cand[n]
|
||||
if not e:
|
||||
raise SyntaxError("Missing operand!")
|
||||
if n % 2:
|
||||
op = typ[e]
|
||||
else:
|
||||
res = op(res, trans(e))
|
||||
n += 1
|
||||
return res
|
||||
#Now replace unary operators - only they are left
|
||||
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
|
||||
else:
|
||||
op = '++'
|
||||
meths = js_post_inc, js_pre_inc
|
||||
pos = cand.index(op)
|
||||
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]
|
||||
meth = meths[1]
|
||||
else:
|
||||
raise SyntaxError('Invalid use of ++ operator')
|
||||
if cand[pos + 2:]:
|
||||
raise SyntaxError('Too many operands')
|
||||
operand = meth(trans(a))
|
||||
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 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 @@....
|
||||
assert new in REPL
|
||||
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, ('#{', '@}'))
|
||||
res = []
|
||||
for e in it:
|
||||
if e[0] != '#' and e[0] != '@':
|
||||
res += [x.strip() for x in e.split('.')]
|
||||
else:
|
||||
res += [e.strip()]
|
||||
# res[0] can be inside @@ (name)...
|
||||
res = filter(lambda x: x, res)
|
||||
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__() + ')'
|
||||
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:
|
||||
return out
|
||||
n = 1
|
||||
while n < len(res): #now every func call is a prop call
|
||||
e = res[n]
|
||||
if e[0] == '@': # direct call
|
||||
out += trans_args(REPL[e])
|
||||
n += 1
|
||||
continue
|
||||
args = False #assume not prop call
|
||||
if n + 1 < len(res) and res[n + 1][0] == '@': #prop call
|
||||
args = trans_args(REPL[res[n + 1]])[1:]
|
||||
if args != ')':
|
||||
args = ',' + args
|
||||
if e[0] == '#':
|
||||
prop = trans(REPL[e][1:-1])
|
||||
else:
|
||||
if not is_lval(e):
|
||||
raise SyntaxError('Invalid identifier: "%s"' % e)
|
||||
prop = e.__repr__()
|
||||
if args: # prop call
|
||||
n += 1
|
||||
out += '.callprop(' + prop + args
|
||||
else: #prop get
|
||||
out += '.get(' + prop + ')'
|
||||
n += 1
|
||||
return out
|
||||
|
||||
|
||||
def js_comma(a, b):
|
||||
return 'PyJsComma(' + a + ',' + b + ')'
|
||||
|
||||
|
||||
def js_or(a, b):
|
||||
return '(' + a + ' or ' + b + ')'
|
||||
|
||||
|
||||
def js_bor(a, b):
|
||||
return '(' + a + '|' + b + ')'
|
||||
|
||||
|
||||
def js_bxor(a, b):
|
||||
return '(' + a + '^' + b + ')'
|
||||
|
||||
|
||||
def js_band(a, b):
|
||||
return '(' + a + '&' + b + ')'
|
||||
|
||||
|
||||
def js_and(a, b):
|
||||
return '(' + a + ' and ' + b + ')'
|
||||
|
||||
|
||||
def js_strict_eq(a, b):
|
||||
|
||||
return 'PyJsStrictEq(' + a + ',' + b + ')'
|
||||
|
||||
|
||||
def js_strict_neq(a, b):
|
||||
return 'PyJsStrictNeq(' + a + ',' + b + ')'
|
||||
|
||||
|
||||
#Not handled by python in the same way like JS. For example 2==2==True returns false.
|
||||
# In JS above would return true so we need brackets.
|
||||
def js_abstract_eq(a, b):
|
||||
return '(' + a + '==' + b + ')'
|
||||
|
||||
|
||||
#just like ==
|
||||
def js_abstract_neq(a, b):
|
||||
return '(' + a + '!=' + b + ')'
|
||||
|
||||
|
||||
def js_lt(a, b):
|
||||
return '(' + a + '<' + b + ')'
|
||||
|
||||
|
||||
def js_le(a, b):
|
||||
return '(' + a + '<=' + b + ')'
|
||||
|
||||
|
||||
def js_ge(a, b):
|
||||
return '(' + a + '>=' + b + ')'
|
||||
|
||||
|
||||
def js_gt(a, b):
|
||||
return '(' + a + '>' + b + ')'
|
||||
|
||||
|
||||
def js_in(a, b):
|
||||
return b + '.contains(' + a + ')'
|
||||
|
||||
|
||||
def js_instanceof(a, b):
|
||||
return a + '.instanceof(' + b + ')'
|
||||
|
||||
|
||||
def js_lshift(a, b):
|
||||
return '(' + a + '<<' + b + ')'
|
||||
|
||||
|
||||
def js_rshift(a, b):
|
||||
return '(' + a + '>>' + b + ')'
|
||||
|
||||
|
||||
def js_shit(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_sub(a, b): # To simplify
|
||||
return '(%s-%s)' % (a, b)
|
||||
|
||||
|
||||
def js_mul(a, b):
|
||||
return '(' + a + '*' + b + ')'
|
||||
|
||||
|
||||
def js_div(a, b):
|
||||
return '(' + a + '/' + b + ')'
|
||||
|
||||
|
||||
def js_mod(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()'
|
||||
|
||||
|
||||
def js_void(a):
|
||||
return '(' + a + ')'
|
||||
|
||||
|
||||
def js_new(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.
|
||||
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
|
||||
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':
|
||||
raise SyntaxError('Invalid delete operation')
|
||||
return beg[:-3] + 'delete' + arglist
|
||||
|
||||
|
||||
def js_neg(a):
|
||||
return '(-' + a + ')'
|
||||
|
||||
|
||||
def js_pos(a):
|
||||
return '(+' + a + ')'
|
||||
|
||||
|
||||
def js_inv(a):
|
||||
return '(~' + a + ')'
|
||||
|
||||
|
||||
def js_not(a):
|
||||
return a + '.neg()'
|
||||
|
||||
|
||||
def postfix(a, inc, post):
|
||||
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,%s%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 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)
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#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}
|
||||
|
||||
#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
|
||||
"""Transforms this ?: crap into if else python syntax"""
|
||||
ind = code.rfind('?')
|
||||
if ind == -1:
|
||||
return code
|
||||
sep = code.find(':', ind)
|
||||
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:])
|
||||
|
||||
|
||||
from code import InteractiveConsole
|
||||
|
||||
#e = InteractiveConsole(globals()).interact()
|
||||
import traceback
|
||||
|
||||
|
||||
def trans(code):
|
||||
return NodeVisitor(code.strip()).translate().strip()
|
||||
|
||||
|
||||
#todo finish this trans args
|
||||
def trans_args(code):
|
||||
new = bracket_replace(code.strip()[1:-1])
|
||||
args = ','.join(trans(e) for e in new.split(','))
|
||||
return '(%s)' % args
|
||||
|
||||
|
||||
EXP = 0
|
||||
|
||||
|
||||
def exp_translator(code):
|
||||
global REPL, EXP
|
||||
EXP += 1
|
||||
REPL = {}
|
||||
#print EXP, code
|
||||
code = code.replace('\n', ' ')
|
||||
assert '@' not in code
|
||||
assert ';' not in code
|
||||
assert '#' not in code
|
||||
#if not code.strip(): #?
|
||||
# return 'var.get("undefined")'
|
||||
try:
|
||||
return trans(code)
|
||||
except:
|
||||
#print '\n\ntrans failed on \n\n' + code
|
||||
#raw_input('\n\npress enter')
|
||||
raise
|
||||
|
||||
|
||||
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++')
|
||||
#First line translated with PyJs: PyJsStrictEq(PyJsAdd((Js(100)*Js(50)),Js(30)), Js("5030")), yay!
|
||||
print exp_translator('delete a.f')
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user