Add JavaScript support

This commit introduce JavaScript as nodes generation script, to procedurally generate input nodes, which usually should draw by user on the canvas.

Powered by QuickJS (https://bellard.org/quickjs/)
master
Jeremy Hu 2019-07-20 14:04:53 +09:30
parent aec5acd793
commit 63d92e7682
75 changed files with 88856 additions and 8 deletions

View File

@ -1143,3 +1143,30 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode
<pre> <pre>
https://github.com/huxingyi/nodemesh https://github.com/huxingyi/nodemesh
</pre> </pre>
<h1>QuickJS</h1>
<pre>
#
# QuickJS Javascript Engine
#
# Copyright (c) 2017-2019 Fabrice Bellard
# Copyright (c) 2017-2019 Charlie Gordon
#
# 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.
</pre>

View File

@ -369,10 +369,41 @@ HEADERS += src/toolmesh.h
HEADERS += src/shadervertex.h HEADERS += src/shadervertex.h
SOURCES += src/scripteditwidget.cpp
HEADERS += src/scripteditwidget.h
SOURCES += src/scriptvariableswidget.cpp
HEADERS += src/scriptvariableswidget.h
SOURCES += src/scriptwidget.cpp
HEADERS += src/scriptwidget.h
SOURCES += src/scriptrunner.cpp
HEADERS += src/scriptrunner.h
SOURCES += src/variablesxml.cpp
HEADERS += src/variablesxml.h
SOURCES += src/main.cpp SOURCES += src/main.cpp
HEADERS += src/version.h HEADERS += src/version.h
INCLUDEPATH += thirdparty/quickjs/quickjs-2019-07-09
DEFINES += "CONFIG_VERSION=\"\\\"2019-07-09\\\"\""
SOURCES += thirdparty/quickjs/quickjs-2019-07-09/quickjs.c
HEADERS += thirdparty/quickjs/quickjs-2019-07-09/quickjs.h
SOURCES += thirdparty/quickjs/quickjs-2019-07-09/cutils.c
HEADERS += thirdparty/quickjs/quickjs-2019-07-09/cutils.h
SOURCES += thirdparty/quickjs/quickjs-2019-07-09/libunicode.c
HEADERS += thirdparty/quickjs/quickjs-2019-07-09/libunicode.h
SOURCES += thirdparty/quickjs/quickjs-2019-07-09/libregexp.c
HEADERS += thirdparty/quickjs/quickjs-2019-07-09/libregexp.h
INCLUDEPATH += thirdparty/nodemesh INCLUDEPATH += thirdparty/nodemesh
SOURCES += thirdparty/nodemesh/nodemesh/wrapper.cpp SOURCES += thirdparty/nodemesh/nodemesh/wrapper.cpp

View File

@ -354,6 +354,10 @@ Tips:
<source>Clear Cut Face</source> <source>Clear Cut Face</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Script</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>ExportPreviewWidget</name> <name>ExportPreviewWidget</name>

View File

@ -61,7 +61,9 @@ Document::Document() :
m_materialPreviewsGenerator(nullptr), m_materialPreviewsGenerator(nullptr),
m_motionsGenerator(nullptr), m_motionsGenerator(nullptr),
m_meshGenerationId(0), m_meshGenerationId(0),
m_nextMeshGenerationId(1) m_nextMeshGenerationId(1),
m_scriptRunner(nullptr),
m_isScriptResultObsolete(false)
{ {
connect(&Preferences::instance(), &Preferences::partColorChanged, this, &Document::applyPreferencePartColorChange); connect(&Preferences::instance(), &Preferences::partColorChanged, this, &Document::applyPreferencePartColorChange);
connect(&Preferences::instance(), &Preferences::flatShadingChanged, this, &Document::applyPreferenceFlatShadingChange); connect(&Preferences::instance(), &Preferences::flatShadingChanged, this, &Document::applyPreferenceFlatShadingChange);
@ -1576,6 +1578,15 @@ void Document::silentReset()
removeRigResults(); removeRigResults();
} }
void Document::silentResetScript()
{
m_script.clear();
m_mergedVariables.clear();
m_cachedVariables.clear();
m_scriptError.clear();
m_scriptConsoleLog.clear();
}
void Document::reset() void Document::reset()
{ {
silentReset(); silentReset();
@ -1583,6 +1594,16 @@ void Document::reset()
emit skeletonChanged(); emit skeletonChanged();
} }
void Document::resetScript()
{
silentResetScript();
emit cleanupScript();
emit scriptChanged();
emit scriptErrorChanged();
emit scriptConsoleLogChanged();
emit mergedVaraiblesChanged();
}
void Document::fromSnapshot(const Snapshot &snapshot) void Document::fromSnapshot(const Snapshot &snapshot)
{ {
reset(); reset();
@ -3306,3 +3327,169 @@ void Document::copyNodes(std::set<QUuid> nodeIdSet) const
QClipboard *clipboard = QApplication::clipboard(); QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(snapshotXml); clipboard->setText(snapshotXml);
} }
void Document::initScript(const QString &script)
{
m_script = script;
emit scriptModifiedFromExternal();
}
void Document::updateScript(const QString &script)
{
if (m_script == script)
return;
m_script = script;
emit scriptChanged();
}
void Document::updateVariable(const QString &name, const std::map<QString, QString> &value)
{
auto variable = m_cachedVariables.find(name);
if (variable == m_cachedVariables.end()) {
qDebug() << "Update a nonexist variable:" << name << "value:" << value;
return;
}
if (variable->second == value)
return;
variable->second = value;
m_mergedVariables[name] = value;
emit mergedVaraiblesChanged();
}
void Document::updateVariableValue(const QString &name, const QString &value)
{
auto variable = m_cachedVariables.find(name);
if (variable == m_cachedVariables.end()) {
qDebug() << "Update a nonexist variable:" << name << "value:" << value;
return;
}
auto &variableValue = variable->second["value"];
if (variableValue == value)
return;
qDebug() << "Update variable:" << name << "from:" << variableValue << "to:" << value;
variableValue = value;
m_mergedVariables[name] = variable->second;
runScript();
}
void Document::updateDefaultVariables(const std::map<QString, std::map<QString, QString>> &defaultVariables)
{
bool updated = false;
for (const auto &it: defaultVariables) {
if (m_mergedVariables.find(it.first) != m_mergedVariables.end())
continue;
updated = true;
auto findCached = m_cachedVariables.find(it.first);
if (findCached != m_cachedVariables.end()) {
m_mergedVariables[it.first] = findCached->second;
} else {
m_mergedVariables[it.first] = it.second;
m_cachedVariables[it.first] = it.second;
}
}
std::vector<QString> eraseList;
for (const auto &it: m_mergedVariables) {
if (defaultVariables.end() == defaultVariables.find(it.first)) {
eraseList.push_back(it.first);
updated = true;
}
}
for (const auto &it: eraseList) {
m_mergedVariables.erase(it);
}
if (updated) {
emit mergedVaraiblesChanged();
}
}
void Document::runScript()
{
if (nullptr != m_scriptRunner) {
m_isScriptResultObsolete = true;
return;
}
m_isScriptResultObsolete = false;
qDebug() << "Script running..";
QThread *thread = new QThread;
m_scriptRunner = new ScriptRunner();
m_scriptRunner->moveToThread(thread);
m_scriptRunner->setScript(new QString(m_script));
m_scriptRunner->setVariables(new std::map<QString, std::map<QString, QString>>(m_mergedVariables));
connect(thread, &QThread::started, m_scriptRunner, &ScriptRunner::process);
connect(m_scriptRunner, &ScriptRunner::finished, this, &Document::scriptResultReady);
connect(m_scriptRunner, &ScriptRunner::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
emit scriptRunning();
thread->start();
}
void Document::scriptResultReady()
{
Snapshot *snapshot = m_scriptRunner->takeResultSnapshot();
std::map<QString, std::map<QString, QString>> *defaultVariables = m_scriptRunner->takeDefaultVariables();
bool errorChanged = false;
bool consoleLogChanged = false;
const QString &scriptError = m_scriptRunner->scriptError();
if (m_scriptError != scriptError) {
m_scriptError = scriptError;
errorChanged = true;
}
const QString &consoleLog = m_scriptRunner->consoleLog();
if (m_scriptConsoleLog != consoleLog) {
m_scriptConsoleLog = consoleLog;
consoleLogChanged = true;
}
if (nullptr != snapshot) {
fromSnapshot(*snapshot);
delete snapshot;
saveSnapshot();
}
if (nullptr != defaultVariables) {
updateDefaultVariables(*defaultVariables);
delete defaultVariables;
}
delete m_scriptRunner;
m_scriptRunner = nullptr;
if (errorChanged)
emit scriptErrorChanged();
if (consoleLogChanged)
emit scriptConsoleLogChanged();
qDebug() << "Script run done";
if (m_isScriptResultObsolete) {
runScript();
}
}
const QString &Document::script() const
{
return m_script;
}
const std::map<QString, std::map<QString, QString>> &Document::variables() const
{
return m_mergedVariables;
}
const QString &Document::scriptError() const
{
return m_scriptError;
}
const QString &Document::scriptConsoleLog() const
{
return m_scriptConsoleLog;
}

View File

@ -26,6 +26,7 @@
#include "skeletondocument.h" #include "skeletondocument.h"
#include "combinemode.h" #include "combinemode.h"
#include "preferences.h" #include "preferences.h"
#include "scriptrunner.h"
class MaterialPreviewsGenerator; class MaterialPreviewsGenerator;
class MotionsGenerator; class MotionsGenerator;
@ -418,6 +419,7 @@ signals:
void partColorSolubilityChanged(QUuid partId); void partColorSolubilityChanged(QUuid partId);
void componentCombineModeChanged(QUuid componentId); void componentCombineModeChanged(QUuid componentId);
void cleanup(); void cleanup();
void cleanupScript();
void originChanged(); void originChanged();
void xlockStateChanged(); void xlockStateChanged();
void ylockStateChanged(); void ylockStateChanged();
@ -461,6 +463,12 @@ signals:
void postProcessing(); void postProcessing();
void textureGenerating(); void textureGenerating();
void textureChanged(); void textureChanged();
void scriptChanged();
void scriptModifiedFromExternal();
void mergedVaraiblesChanged();
void scriptRunning();
void scriptErrorChanged();
void scriptConsoleLogChanged();
public: // need initialize public: // need initialize
QImage *textureGuideImage; QImage *textureGuideImage;
QImage *textureImage; QImage *textureImage;
@ -525,6 +533,10 @@ public:
const Outcome &currentRiggedOutcome() const; const Outcome &currentRiggedOutcome() const;
bool currentRigSucceed() const; bool currentRigSucceed() const;
bool isMeshGenerating() const; bool isMeshGenerating() const;
const QString &script() const;
const std::map<QString, std::map<QString, QString>> &variables() const;
const QString &scriptError() const;
const QString &scriptConsoleLog() const;
public slots: public slots:
void undo() override; void undo() override;
void redo() override; void redo() override;
@ -612,8 +624,10 @@ public slots:
void batchChangeBegin(); void batchChangeBegin();
void batchChangeEnd(); void batchChangeEnd();
void reset(); void reset();
void resetScript();
void clearHistories(); void clearHistories();
void silentReset(); void silentReset();
void silentResetScript();
void breakEdge(QUuid edgeId); void breakEdge(QUuid edgeId);
void setXlockState(bool locked); void setXlockState(bool locked);
void setYlockState(bool locked); void setYlockState(bool locked);
@ -642,6 +656,13 @@ public slots:
void renameMaterial(QUuid materialId, QString name); void renameMaterial(QUuid materialId, QString name);
void applyPreferencePartColorChange(); void applyPreferencePartColorChange();
void applyPreferenceFlatShadingChange(); void applyPreferenceFlatShadingChange();
void initScript(const QString &script);
void updateScript(const QString &script);
void updateDefaultVariables(const std::map<QString, std::map<QString, QString>> &defaultVariables);
void runScript();
void scriptResultReady();
void updateVariable(const QString &name, const std::map<QString, QString> &value);
void updateVariableValue(const QString &name, const QString &value);
private: private:
void splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId); void splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId);
void joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId=QUuid()); void joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId=QUuid());
@ -691,6 +712,13 @@ private: // need initialize
MotionsGenerator *m_motionsGenerator; MotionsGenerator *m_motionsGenerator;
quint64 m_meshGenerationId; quint64 m_meshGenerationId;
quint64 m_nextMeshGenerationId; quint64 m_nextMeshGenerationId;
QString m_script;
std::map<QString, std::map<QString, QString>> m_cachedVariables;
std::map<QString, std::map<QString, QString>> m_mergedVariables;
ScriptRunner *m_scriptRunner;
bool m_isScriptResultObsolete;
QString m_scriptError;
QString m_scriptConsoleLog;
private: private:
static unsigned long m_maxSnapshot; static unsigned long m_maxSnapshot;
std::deque<HistoryItem> m_undoItems; std::deque<HistoryItem> m_undoItems;

View File

@ -39,6 +39,8 @@
#include "shortcuts.h" #include "shortcuts.h"
#include "floatnumberwidget.h" #include "floatnumberwidget.h"
#include "cutfacelistwidget.h" #include "cutfacelistwidget.h"
#include "scriptwidget.h"
#include "variablesxml.h"
int DocumentWindow::m_modelRenderWidgetInitialX = 16; int DocumentWindow::m_modelRenderWidgetInitialX = 16;
int DocumentWindow::m_modelRenderWidgetInitialY = 16; int DocumentWindow::m_modelRenderWidgetInitialY = 16;
@ -325,10 +327,17 @@ DocumentWindow::DocumentWindow() :
connect(motionManageWidget, &MotionManageWidget::unregisterDialog, this, &DocumentWindow::unregisterDialog); connect(motionManageWidget, &MotionManageWidget::unregisterDialog, this, &DocumentWindow::unregisterDialog);
addDockWidget(Qt::RightDockWidgetArea, motionDocker); addDockWidget(Qt::RightDockWidgetArea, motionDocker);
QDockWidget *scriptDocker = new QDockWidget(tr("Script"), this);
scriptDocker->setAllowedAreas(Qt::RightDockWidgetArea);
ScriptWidget *scriptWidget = new ScriptWidget(m_document, scriptDocker);
scriptDocker->setWidget(scriptWidget);
addDockWidget(Qt::RightDockWidgetArea, scriptDocker);
tabifyDockWidget(partTreeDocker, materialDocker); tabifyDockWidget(partTreeDocker, materialDocker);
tabifyDockWidget(materialDocker, rigDocker); tabifyDockWidget(materialDocker, rigDocker);
tabifyDockWidget(rigDocker, poseDocker); tabifyDockWidget(rigDocker, poseDocker);
tabifyDockWidget(poseDocker, motionDocker); tabifyDockWidget(poseDocker, motionDocker);
tabifyDockWidget(motionDocker, scriptDocker);
partTreeDocker->raise(); partTreeDocker->raise();
@ -619,12 +628,6 @@ DocumentWindow::DocumentWindow() :
}); });
m_viewMenu->addAction(m_toggleWireframeAction); m_viewMenu->addAction(m_toggleWireframeAction);
//m_toggleSmoothNormalAction = new QAction(tr("Toggle Smooth"), this);
//connect(m_toggleSmoothNormalAction, &QAction::triggered, [=]() {
// m_document->toggleSmoothNormal();
//});
//m_viewMenu->addAction(m_toggleSmoothNormalAction);
connect(m_viewMenu, &QMenu::aboutToShow, [=]() { connect(m_viewMenu, &QMenu::aboutToShow, [=]() {
m_resetModelWidgetPosAction->setEnabled(!isModelSitInVisibleArea(m_modelRenderWidget)); m_resetModelWidgetPosAction->setEnabled(!isModelSitInVisibleArea(m_modelRenderWidget));
}); });
@ -666,6 +669,13 @@ DocumentWindow::DocumentWindow() :
}); });
m_windowMenu->addAction(m_showMotionsAction); m_windowMenu->addAction(m_showMotionsAction);
m_showScriptAction = new QAction(tr("Script"), this);
connect(m_showScriptAction, &QAction::triggered, [=]() {
scriptDocker->show();
scriptDocker->raise();
});
m_windowMenu->addAction(m_showScriptAction);
QMenu *dialogsMenu = m_windowMenu->addMenu(tr("Dialogs")); QMenu *dialogsMenu = m_windowMenu->addMenu(tr("Dialogs"));
connect(dialogsMenu, &QMenu::aboutToShow, [=]() { connect(dialogsMenu, &QMenu::aboutToShow, [=]() {
dialogsMenu->clear(); dialogsMenu->clear();
@ -950,6 +960,7 @@ DocumentWindow::DocumentWindow() :
connect(m_document, &Document::turnaroundChanged, this, &DocumentWindow::documentChanged); connect(m_document, &Document::turnaroundChanged, this, &DocumentWindow::documentChanged);
connect(m_document, &Document::optionsChanged, this, &DocumentWindow::documentChanged); connect(m_document, &Document::optionsChanged, this, &DocumentWindow::documentChanged);
connect(m_document, &Document::rigChanged, this, &DocumentWindow::documentChanged); connect(m_document, &Document::rigChanged, this, &DocumentWindow::documentChanged);
connect(m_document, &Document::scriptChanged, this, &DocumentWindow::documentChanged);
connect(m_modelRenderWidget, &ModelWidget::customContextMenuRequested, [=](const QPoint &pos) { connect(m_modelRenderWidget, &ModelWidget::customContextMenuRequested, [=](const QPoint &pos) {
graphicsWidget->showContextMenu(graphicsWidget->mapFromGlobal(m_modelRenderWidget->mapToGlobal(pos))); graphicsWidget->showContextMenu(graphicsWidget->mapFromGlobal(m_modelRenderWidget->mapToGlobal(pos)));
@ -989,6 +1000,9 @@ DocumentWindow::DocumentWindow() :
m_document->generateMaterialPreviews(); m_document->generateMaterialPreviews();
}); });
connect(m_document, &Document::scriptChanged, m_document, &Document::runScript);
connect(m_document, &Document::scriptModifiedFromExternal, m_document, &Document::runScript);
initShortCuts(this, m_graphicsWidget); initShortCuts(this, m_graphicsWidget);
connect(this, &DocumentWindow::initialized, m_document, &Document::uiReady); connect(this, &DocumentWindow::initialized, m_document, &Document::uiReady);
@ -1067,6 +1081,7 @@ void DocumentWindow::newDocument()
return; return;
} }
m_document->clearHistories(); m_document->clearHistories();
m_document->resetScript();
m_document->reset(); m_document->reset();
m_document->saveSnapshot(); m_document->saveSnapshot();
} }
@ -1213,6 +1228,20 @@ void DocumentWindow::saveTo(const QString &saveAsFilename)
ds3Writer.add("canvas.png", "asset", &m_document->turnaroundPngByteArray); ds3Writer.add("canvas.png", "asset", &m_document->turnaroundPngByteArray);
} }
if (!m_document->script().isEmpty()) {
auto script = m_document->script().toUtf8();
ds3Writer.add("model.js", "script", &script);
}
const auto &variables = m_document->variables();
if (!variables.empty()) {
QByteArray variablesXml;
QXmlStreamWriter variablesXmlStream(&variablesXml);
saveVariablesToXmlStream(variables, &variablesXmlStream);
if (variablesXml.size() > 0)
ds3Writer.add("variables.xml", "variable", &variablesXml);
}
std::set<QUuid> imageIds; std::set<QUuid> imageIds;
for (const auto &material: snapshot.materials) { for (const auto &material: snapshot.materials) {
for (auto &layer: material.second) { for (auto &layer: material.second) {
@ -1264,6 +1293,7 @@ void DocumentWindow::openExample(const QString &modelName)
Ds3FileReader ds3Reader(":/resources/" + modelName); Ds3FileReader ds3Reader(":/resources/" + modelName);
m_document->clearHistories(); m_document->clearHistories();
m_document->resetScript();
m_document->reset(); m_document->reset();
m_document->saveSnapshot(); m_document->saveSnapshot();
@ -1328,6 +1358,7 @@ void DocumentWindow::open()
Ds3FileReader ds3Reader(filename); Ds3FileReader ds3Reader(filename);
m_document->clearHistories(); m_document->clearHistories();
m_document->resetScript();
m_document->reset(); m_document->reset();
m_document->saveSnapshot(); m_document->saveSnapshot();
@ -1365,6 +1396,22 @@ void DocumentWindow::open()
QImage image = QImage::fromData(data, "PNG"); QImage image = QImage::fromData(data, "PNG");
m_document->updateTurnaround(image); m_document->updateTurnaround(image);
} }
} else if (item.type == "script") {
if (item.name == "model.js") {
QByteArray script;
ds3Reader.loadItem(item.name, &script);
m_document->initScript(QString::fromUtf8(script.constData()));
}
} else if (item.type == "variable") {
if (item.name == "variables.xml") {
QByteArray data;
ds3Reader.loadItem(item.name, &data);
QXmlStreamReader stream(data);
std::map<QString, std::map<QString, QString>> variables;
loadVariablesFromXmlStream(&variables, stream);
for (const auto &it: variables)
m_document->updateVariable(it.first, it.second);
}
} }
} }
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();

View File

@ -143,7 +143,6 @@ private:
QMenu *m_viewMenu; QMenu *m_viewMenu;
QAction *m_resetModelWidgetPosAction; QAction *m_resetModelWidgetPosAction;
QAction *m_toggleWireframeAction; QAction *m_toggleWireframeAction;
//QAction *m_toggleSmoothNormalAction;
QAction *m_showMotionsListAction; QAction *m_showMotionsListAction;
QMenu *m_windowMenu; QMenu *m_windowMenu;
@ -153,6 +152,7 @@ private:
QAction *m_showRigAction; QAction *m_showRigAction;
QAction *m_showPosesAction; QAction *m_showPosesAction;
QAction *m_showMotionsAction; QAction *m_showMotionsAction;
QAction *m_showScriptAction;
QMenu *m_helpMenu; QMenu *m_helpMenu;
QAction *m_gotoHomepageAction; QAction *m_gotoHomepageAction;

153
src/scripteditwidget.cpp Normal file
View File

@ -0,0 +1,153 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtWidgets>
#include "theme.h"
#include "scripteditwidget.h"
ScriptEditWidget::ScriptEditWidget(QWidget *parent) : QPlainTextEdit(parent)
{
connect(this, &QPlainTextEdit::textChanged, this, [&]() {
emit scriptChanged(toPlainText());
});
m_lineNumberArea = new LineNumberArea(this);
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
updateLineNumberAreaWidth(0);
highlightCurrentLine();
}
int ScriptEditWidget::lineNumberAreaWidth()
{
int digits = 1;
int max = qMax(1, blockCount());
while (max >= 10) {
max /= 10;
++digits;
}
int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
return space;
}
void ScriptEditWidget::updateLineNumberAreaWidth(int /* newBlockCount */)
{
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}
void ScriptEditWidget::updateLineNumberArea(const QRect &rect, int dy)
{
if (dy)
m_lineNumberArea->scroll(0, dy);
else
m_lineNumberArea->update(0, rect.y(), m_lineNumberArea->width(), rect.height());
if (rect.contains(viewport()->rect()))
updateLineNumberAreaWidth(0);
}
void ScriptEditWidget::resizeEvent(QResizeEvent *e)
{
QPlainTextEdit::resizeEvent(e);
QRect cr = contentsRect();
m_lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
void ScriptEditWidget::highlightCurrentLine()
{
QList<QTextEdit::ExtraSelection> extraSelections;
if (!isReadOnly()) {
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(0x24, 0x24, 0x24).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
extraSelections.append(selection);
}
setExtraSelections(extraSelections);
}
void ScriptEditWidget::lineNumberAreaPaintEvent(QPaintEvent *event)
{
QPainter painter(m_lineNumberArea);
painter.fillRect(event->rect(), QColor(0x24, 0x24, 0x24));
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + (int) blockBoundingRect(block).height();
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
QString number = QString::number(blockNumber + 1);
painter.setPen(Theme::white);
painter.drawText(0, top, m_lineNumberArea->width(), fontMetrics().height(),
Qt::AlignRight, number);
}
block = block.next();
top = bottom;
bottom = top + (int) blockBoundingRect(block).height();
++blockNumber;
}
}

99
src/scripteditwidget.h Normal file
View File

@ -0,0 +1,99 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef CODEEDITOR_H
#define CODEEDITOR_H
#include <QPlainTextEdit>
#include <QObject>
class QPaintEvent;
class QResizeEvent;
class QSize;
class QWidget;
class LineNumberArea;
class ScriptEditWidget : public QPlainTextEdit
{
Q_OBJECT
signals:
void scriptChanged(const QString &script);
public:
ScriptEditWidget(QWidget *parent=0);
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
protected:
void resizeEvent(QResizeEvent *event);
private slots:
void updateLineNumberAreaWidth(int newBlockCount);
void highlightCurrentLine();
void updateLineNumberArea(const QRect &, int);
private:
QWidget *m_lineNumberArea;
};
class LineNumberArea : public QWidget
{
public:
LineNumberArea(ScriptEditWidget *editor) : QWidget(editor) {
m_codeEditor = editor;
}
QSize sizeHint() const {
return QSize(m_codeEditor->lineNumberAreaWidth(), 0);
}
protected:
void paintEvent(QPaintEvent *event) {
m_codeEditor->lineNumberAreaPaintEvent(event);
}
private:
ScriptEditWidget *m_codeEditor;
};
#endif

569
src/scriptrunner.cpp Normal file
View File

@ -0,0 +1,569 @@
#include <QDebug>
#include <QElapsedTimer>
#include <QUuid>
#include "scriptrunner.h"
JSClassID ScriptRunner::js_partClassId = 0;
JSClassID ScriptRunner::js_componentClassId = 0;
JSClassID ScriptRunner::js_nodeClassId = 0;
static JSValue js_print(JSContext *context, JSValueConst thisValue,
int argc, JSValueConst *argv)
{
ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context);
int i;
const char *str;
for (i = 0; i < argc; i++) {
if (i != 0)
runner->consoleLog() += ' ';
str = JS_ToCString(context, argv[i]);
if (!str)
return JS_EXCEPTION;
runner->consoleLog() += str;
JS_FreeCString(context, str);
}
runner->consoleLog() += '\n';
return JS_UNDEFINED;
}
static JSValue js_setAttribute(JSContext *context, JSValueConst thisValue,
int argc, JSValueConst *argv)
{
ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context);
if (argc < 3) {
runner->consoleLog() += "Incomplete parameters, expect: element, attributeName, attributeValue\r\n";
return JS_EXCEPTION;
}
ScriptRunner::DocumentElement *element = nullptr;
if (nullptr == element) {
ScriptRunner::DocumentNode *node = (ScriptRunner::DocumentNode *)JS_GetOpaque(argv[0],
ScriptRunner::js_nodeClassId);
if (nullptr != node) {
element = (ScriptRunner::DocumentElement *)node;
}
}
if (nullptr == element) {
ScriptRunner::DocumentPart *part = (ScriptRunner::DocumentPart *)JS_GetOpaque(argv[0],
ScriptRunner::js_partClassId);
if (nullptr != part) {
element = (ScriptRunner::DocumentElement *)part;
}
}
if (nullptr == element) {
ScriptRunner::DocumentComponent *component = (ScriptRunner::DocumentComponent *)JS_GetOpaque(argv[0],
ScriptRunner::js_componentClassId);
if (nullptr != component) {
element = (ScriptRunner::DocumentComponent *)component;
}
}
if (nullptr == element) {
runner->consoleLog() += "Parameters error\r\n";
return JS_EXCEPTION;
}
const char *attributeName = nullptr;
const char *attributeValue = nullptr;
attributeName = JS_ToCString(context, argv[1]);
if (!attributeName)
goto fail;
attributeValue = JS_ToCString(context, argv[2]);
if (!attributeValue)
goto fail;
runner->setAttribute(element, attributeName, attributeValue);
JS_FreeCString(context, attributeName);
JS_FreeCString(context, attributeValue);
return JS_UNDEFINED;
fail:
JS_FreeCString(context, attributeName);
JS_FreeCString(context, attributeValue);
return JS_EXCEPTION;
}
static JSValue js_connect(JSContext *context, JSValueConst thisValue,
int argc, JSValueConst *argv)
{
ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context);
if (argc < 2) {
runner->consoleLog() += "Incomplete parameters, expect: firstNode, secondNode\r\n";
return JS_EXCEPTION;
}
ScriptRunner::DocumentElement *firstElement = (ScriptRunner::DocumentElement *)JS_GetOpaque(argv[0],
ScriptRunner::js_nodeClassId);
if (nullptr == firstElement ||
ScriptRunner::DocumentElementType::Node != firstElement->type) {
runner->consoleLog() += "First parameter must be node\r\n";
return JS_EXCEPTION;
}
ScriptRunner::DocumentElement *secondElement = (ScriptRunner::DocumentElement *)JS_GetOpaque(argv[1],
ScriptRunner::js_nodeClassId);
if (nullptr == secondElement ||
ScriptRunner::DocumentElementType::Node != secondElement->type) {
runner->consoleLog() += "Second parameter must be node\r\n";
return JS_EXCEPTION;
}
ScriptRunner::DocumentNode *firstNode = (ScriptRunner::DocumentNode *)firstElement;
ScriptRunner::DocumentNode *secondNode = (ScriptRunner::DocumentNode *)secondElement;
if (firstNode->part != secondNode->part) {
runner->consoleLog() += "Cannot connect nodes come from different parts\r\n";
return JS_EXCEPTION;
}
runner->connect(firstNode, secondNode);
return JS_UNDEFINED;
}
static JSValue js_createComponent(JSContext *context, JSValueConst thisValue,
int argc, JSValueConst *argv)
{
ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context);
ScriptRunner::DocumentComponent *parentComponent = nullptr;
if (argc >= 1) {
ScriptRunner::DocumentElement *element = (ScriptRunner::DocumentElement *)JS_GetOpaque(argv[0],
ScriptRunner::js_componentClassId);
if (nullptr == element ||
ScriptRunner::DocumentElementType::Component != element->type) {
runner->consoleLog() += "First parameter must be null or component\r\n";
return JS_EXCEPTION;
}
parentComponent = (ScriptRunner::DocumentComponent *)element;
}
JSValue component = JS_NewObjectClass(context, ScriptRunner::js_componentClassId);
JS_SetOpaque(component, runner->createComponent(parentComponent));
return component;
}
static JSValue js_createPart(JSContext *context, JSValueConst thisValue,
int argc, JSValueConst *argv)
{
ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context);
if (argc < 1) {
runner->consoleLog() += "Incomplete parameters, expect: component\r\n";
return JS_EXCEPTION;
}
ScriptRunner::DocumentElement *element = (ScriptRunner::DocumentElement *)JS_GetOpaque(argv[0],
ScriptRunner::js_componentClassId);
if (nullptr == element ||
ScriptRunner::DocumentElementType::Component != element->type) {
runner->consoleLog() += "First parameter must be component\r\n";
return JS_EXCEPTION;
}
ScriptRunner::DocumentComponent *component = (ScriptRunner::DocumentComponent *)element;
JSValue part = JS_NewObjectClass(context, ScriptRunner::js_partClassId);
JS_SetOpaque(part, runner->createPart(component));
return part;
}
static JSValue js_createNode(JSContext *context, JSValueConst thisValue,
int argc, JSValueConst *argv)
{
ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context);
if (argc < 1) {
runner->consoleLog() += "Incomplete parameters, expect: part\r\n";
return JS_EXCEPTION;
}
ScriptRunner::DocumentElement *element = (ScriptRunner::DocumentElement *)JS_GetOpaque(argv[0],
ScriptRunner::js_partClassId);
if (nullptr == element ||
ScriptRunner::DocumentElementType::Part != element->type) {
runner->consoleLog() += "First parameter must be part\r\n";
return JS_EXCEPTION;
}
ScriptRunner::DocumentPart *part = (ScriptRunner::DocumentPart *)element;
JSValue node = JS_NewObjectClass(context, ScriptRunner::js_nodeClassId);
JS_SetOpaque(node, runner->createNode(part));
return node;
}
static JSValue js_createVariable(JSContext *context, JSValueConst thisValue,
int argc, JSValueConst *argv)
{
ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context);
if (argc < 2) {
runner->consoleLog() += "Incomplete parameters, expect: name, value\r\n";
return JS_EXCEPTION;
}
QString mergedValue;
const char *name = nullptr;
const char *defaultValue = nullptr;
name = JS_ToCString(context, argv[0]);
if (!name)
goto fail;
defaultValue = JS_ToCString(context, argv[1]);
if (!defaultValue)
goto fail;
mergedValue = runner->createVariable(name, defaultValue);
JS_FreeCString(context, name);
JS_FreeCString(context, defaultValue);
return JS_NewString(context, mergedValue.toUtf8().constData());
fail:
JS_FreeCString(context, name);
JS_FreeCString(context, defaultValue);
return JS_EXCEPTION;
}
ScriptRunner::ScriptRunner()
{
}
ScriptRunner::~ScriptRunner()
{
delete m_resultSnapshot;
delete m_defaultVariables;
for (auto &it: m_parts)
delete it;
for (auto &it: m_components)
delete it;
for (auto &it: m_nodes)
delete it;
}
QString &ScriptRunner::consoleLog()
{
return m_consoleLog;
}
const QString &ScriptRunner::scriptError()
{
return m_scriptError;
}
ScriptRunner::DocumentPart *ScriptRunner::createPart(DocumentComponent *component)
{
ScriptRunner::DocumentPart *part = new ScriptRunner::DocumentPart;
part->component = component;
m_parts.push_back(part);
return part;
}
ScriptRunner::DocumentComponent *ScriptRunner::createComponent(DocumentComponent *parentComponent)
{
ScriptRunner::DocumentComponent *component = new ScriptRunner::DocumentComponent;
component->parentComponent = parentComponent;
m_components.push_back(component);
return component;
}
ScriptRunner::DocumentNode *ScriptRunner::createNode(DocumentPart *part)
{
ScriptRunner::DocumentNode *node = new ScriptRunner::DocumentNode;
node->part = part;
m_nodes.push_back(node);
return node;
}
bool ScriptRunner::setAttribute(DocumentElement *element, const QString &name, const QString &value)
{
element->attributes[name] = value;
// TODO: Validate attribute name and value
return true;
}
void ScriptRunner::connect(DocumentNode *firstNode, DocumentNode *secondNode)
{
m_edges.push_back(std::make_pair(firstNode, secondNode));
}
static void js_componentFinalizer(JSRuntime *runtime, JSValue value)
{
ScriptRunner::DocumentComponent *component = (ScriptRunner::DocumentComponent *)JS_GetOpaque(value,
ScriptRunner::js_componentClassId);
if (component) {
component->deleted = true;
}
}
static JSClassDef js_componentClass = {
"Component",
js_componentFinalizer,
nullptr,
nullptr,
nullptr,
};
static void js_nodeFinalizer(JSRuntime *runtime, JSValue value)
{
ScriptRunner::DocumentNode *node = (ScriptRunner::DocumentNode *)JS_GetOpaque(value,
ScriptRunner::js_nodeClassId);
if (node) {
node->deleted = true;
}
}
static JSClassDef js_nodeClass = {
"Node",
.finalizer = js_nodeFinalizer,
};
static void js_partFinalizer(JSRuntime *runtime, JSValue value)
{
ScriptRunner::DocumentPart *part = (ScriptRunner::DocumentPart *)JS_GetOpaque(value,
ScriptRunner::js_partClassId);
if (part) {
part->deleted = true;
}
}
static JSClassDef js_partClass = {
"Part",
.finalizer = js_partFinalizer,
};
void ScriptRunner::run()
{
QElapsedTimer countTimeConsumed;
countTimeConsumed.start();
// Warning: Not thread safe, but we have only one script instance running, so it doesn't matter
js_partClassId = JS_NewClassID(&js_partClassId);
js_componentClassId = JS_NewClassID(&js_componentClassId);
js_nodeClassId = JS_NewClassID(&js_nodeClassId);
m_defaultVariables = new std::map<QString, std::map<QString, QString>>;
JSRuntime *runtime = JS_NewRuntime();
JSContext *context = JS_NewContext(runtime);
JS_NewClass(runtime, js_partClassId, &js_partClass);
JS_NewClass(runtime, js_componentClassId, &js_componentClass);
JS_NewClass(runtime, js_nodeClassId, &js_nodeClass);
JS_SetContextOpaque(context, this);
if (nullptr != m_script &&
!m_script->isEmpty()) {
auto buffer = m_script->toUtf8();
JSValue globalObject = JS_GetGlobalObject(context);
JSValue document = JS_NewObject(context);
JS_SetPropertyStr(context,
document, "createComponent",
JS_NewCFunction(context, js_createComponent, "createComponent", 1));
JS_SetPropertyStr(context,
document, "createPart",
JS_NewCFunction(context, js_createPart, "createPart", 1));
JS_SetPropertyStr(context,
document, "createNode",
JS_NewCFunction(context, js_createNode, "createNode", 1));
JS_SetPropertyStr(context,
document, "createVariable",
JS_NewCFunction(context, js_createVariable, "createVariable", 2));
JS_SetPropertyStr(context,
document, "connect",
JS_NewCFunction(context, js_connect, "connect", 2));
JS_SetPropertyStr(context,
document, "setAttribute",
JS_NewCFunction(context, js_setAttribute, "setAttribute", 3));
JS_SetPropertyStr(context, globalObject, "document", document);
JSValue console = JS_NewObject(context);
JS_SetPropertyStr(context,
console, "log",
JS_NewCFunction(context, js_print, "log", 1));
JS_SetPropertyStr(context, globalObject, "console", console);
JS_FreeValue(context, globalObject);
JSValue object = JS_Eval(context, buffer.constData(), buffer.size(), "",
JS_EVAL_TYPE_GLOBAL);
if (JS_IsException(object)) {
JSValue exceptionValue = JS_GetException(context);
bool isError = JS_IsError(context, exceptionValue);
if (isError) {
m_scriptError += "Throw: ";
const char *exceptionError = JS_ToCString(context, exceptionValue);
m_scriptError += exceptionError;
m_scriptError += "\r\n";
JS_FreeCString(context, exceptionError);
JSValue value = JS_GetPropertyStr(context, exceptionValue, "stack");
if (!JS_IsUndefined(value)) {
const char *stack = JS_ToCString(context, value);
m_scriptError += stack;
m_scriptError += "\r\n";
JS_FreeCString(context, stack);
}
JS_FreeValue(context, value);
qDebug() << "QuickJS" << m_scriptError;
}
} else {
generateSnapshot();
const char *objectString = JS_ToCString(context, object);
qDebug() << "Result:" << objectString;
JS_FreeCString(context, objectString);
}
JS_FreeValue(context, object);
}
JS_FreeContext(context);
JS_FreeRuntime(runtime);
qDebug() << "The script run" << countTimeConsumed.elapsed() << "milliseconds";
}
void ScriptRunner::generateSnapshot()
{
m_resultSnapshot = new Snapshot;
std::map<void *, QString> pointerToIdMap;
std::map<void *, QStringList> componentChildrensMap;
QStringList rootChildren;
for (const auto &it: m_components) {
QString idString = QUuid::createUuid().toString();
pointerToIdMap[it] = idString;
auto &component = m_resultSnapshot->components[idString];
component = it->attributes;
component["id"] = idString;
componentChildrensMap[it->parentComponent].append(idString);
if (nullptr == it->parentComponent)
rootChildren.append(idString);
}
m_resultSnapshot->rootComponent["children"] = rootChildren.join(",");
for (const auto &it: m_components) {
const auto &idString = pointerToIdMap[it];
auto &component = m_resultSnapshot->components[idString];
auto &childrens = componentChildrensMap[it];
if (!childrens.empty())
component["children"] = childrens.join(",");
}
for (const auto &it: m_parts) {
auto findComponent = pointerToIdMap.find(it->component);
if (findComponent == pointerToIdMap.end()) {
m_scriptError += "Find component pointer failed, component maybe deleted\r\n";
continue;
}
QString idString = QUuid::createUuid().toString();
pointerToIdMap[it] = idString;
auto &part = m_resultSnapshot->parts[idString];
part = it->attributes;
part.insert({"visible", "true"});
part["id"] = idString;
auto &component = m_resultSnapshot->components[findComponent->second];
component["linkData"] = idString;
component["linkDataType"] = "partId";
}
for (const auto &it: m_nodes) {
auto findPart = pointerToIdMap.find(it->part);
if (findPart == pointerToIdMap.end()) {
m_scriptError += "Find part pointer failed, part maybe deleted\r\n";
continue;
}
QString idString = QUuid::createUuid().toString();
pointerToIdMap[it] = idString;
auto &node = m_resultSnapshot->nodes[idString];
node = it->attributes;
node["id"] = idString;
node["partId"] = findPart->second;
}
for (const auto &it: m_edges) {
if (it.first->part != it.second->part) {
m_scriptError += "Cannot connect nodes come from different parts\r\n";
continue;
}
auto findFirstNode = pointerToIdMap.find(it.first);
if (findFirstNode == pointerToIdMap.end()) {
m_scriptError += "Find first node pointer failed, node maybe deleted\r\n";
continue;
}
auto findSecondNode = pointerToIdMap.find(it.second);
if (findSecondNode == pointerToIdMap.end()) {
m_scriptError += "Find second node pointer failed, node maybe deleted\r\n";
continue;
}
auto findPart = pointerToIdMap.find(it.first->part);
if (findPart == pointerToIdMap.end()) {
m_scriptError += "Find part pointer failed, part maybe deleted\r\n";
continue;
}
QString idString = QUuid::createUuid().toString();
auto &edge = m_resultSnapshot->edges[idString];
edge["id"] = idString;
edge["from"] = findFirstNode->second;
edge["to"] = findSecondNode->second;
edge["partId"] = findPart->second;
}
for (const auto &it: m_resultSnapshot->nodes) {
qDebug() << "Generated node:" << it.second;
}
for (const auto &it: m_resultSnapshot->edges) {
qDebug() << "Generated edge:" << it.second;
}
for (const auto &it: m_resultSnapshot->parts) {
qDebug() << "Generated part:" << it.second;
}
for (const auto &it: m_resultSnapshot->components) {
qDebug() << "Generated component:" << it.second;
}
}
QString ScriptRunner::createVariable(const QString &name, const QString &defaultValue)
{
if (nullptr != m_defaultVariables) {
if (m_defaultVariables->find(name) != m_defaultVariables->end()) {
m_scriptError += "Repeated variable name found: \"" + name + "\"";
}
(*m_defaultVariables)[name]["value"] = defaultValue;
}
if (nullptr != m_variables) {
auto findVariable = m_variables->find(name);
if (findVariable != m_variables->end()) {
auto findValue = findVariable->second.find("value");
if (findValue != findVariable->second.end())
return findValue->second;
}
}
return defaultValue;
}
void ScriptRunner::process()
{
run();
emit finished();
}
void ScriptRunner::setScript(QString *script)
{
m_script = script;
}
void ScriptRunner::setVariables(std::map<QString, std::map<QString, QString>> *variables)
{
m_variables = variables;
}
Snapshot *ScriptRunner::takeResultSnapshot()
{
Snapshot *snapshot = m_resultSnapshot;
m_resultSnapshot = nullptr;
return snapshot;
}
std::map<QString, std::map<QString, QString>> *ScriptRunner::takeDefaultVariables()
{
std::map<QString, std::map<QString, QString>> *defaultVariables = m_defaultVariables;
m_defaultVariables = nullptr;
return defaultVariables;
}
void ScriptRunner::mergeVaraibles(std::map<QString, std::map<QString, QString>> *target, const std::map<QString, std::map<QString, QString>> &source)
{
for (const auto &it: source) {
(*target)[it.first] = it.second;
}
}

93
src/scriptrunner.h Normal file
View File

@ -0,0 +1,93 @@
#ifndef DUST3D_SCRIPT_RUNNER_H
#define DUST3D_SCRIPT_RUNNER_H
#include <QObject>
#include "snapshot.h"
extern "C" {
#include "quickjs.h"
}
class ScriptRunner : public QObject
{
Q_OBJECT
public:
enum class DocumentElementType
{
Unknown = 0,
Component,
Part,
Node
};
struct DocumentElement
{
DocumentElementType type = DocumentElementType::Unknown;
bool deleted = false;
std::map<QString, QString> attributes;
};
struct DocumentComponent : DocumentElement
{
DocumentComponent()
{
type = DocumentElementType::Component;
}
DocumentComponent *parentComponent = nullptr;
};
struct DocumentPart : DocumentElement
{
DocumentPart()
{
type = DocumentElementType::Part;
}
DocumentComponent *component = nullptr;
};
struct DocumentNode : DocumentElement
{
DocumentNode()
{
type = DocumentElementType::Node;
}
DocumentPart *part = nullptr;
};
ScriptRunner();
~ScriptRunner();
void run();
void setScript(QString *script);
void setVariables(std::map<QString, std::map<QString, QString>> *variables);
Snapshot *takeResultSnapshot();
std::map<QString, std::map<QString, QString>> *takeDefaultVariables();
const QString &scriptError();
static void mergeVaraibles(std::map<QString, std::map<QString, QString>> *target, const std::map<QString, std::map<QString, QString>> &source);
QString createVariable(const QString &name, const QString &defaultValue);
DocumentPart *createPart(DocumentComponent *component);
DocumentComponent *createComponent(DocumentComponent *parentComponent);
DocumentNode *createNode(DocumentPart *part);
bool setAttribute(DocumentElement *element, const QString &name, const QString &value);
void connect(DocumentNode *firstNode, DocumentNode *secondNode);
QString &consoleLog();
signals:
void finished();
public slots:
void process();
private:
QString *m_script = nullptr;
Snapshot *m_resultSnapshot = nullptr;
std::map<QString, std::map<QString, QString>> *m_defaultVariables = nullptr;
std::map<QString, std::map<QString, QString>> *m_variables = nullptr;
std::vector<DocumentPart *> m_parts;
std::vector<DocumentComponent *> m_components;
std::vector<DocumentNode *> m_nodes;
std::vector<std::pair<DocumentNode *, DocumentNode *>> m_edges;
QString m_scriptError;
QString m_consoleLog;
void generateSnapshot();
public:
static JSClassID js_partClassId;
static JSClassID js_componentClassId;
static JSClassID js_nodeClassId;
};
#endif

View File

@ -0,0 +1,43 @@
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QDebug>
#include "scriptvariableswidget.h"
#include "util.h"
#include "theme.h"
ScriptVariablesWidget::ScriptVariablesWidget(const Document *document,
QWidget *parent) :
QScrollArea(parent),
m_document(document)
{
connect(this, &ScriptVariablesWidget::updateVariableValue, m_document, &Document::updateVariableValue);
setWidgetResizable(true);
reload();
}
void ScriptVariablesWidget::reload()
{
QWidget *widget = new QWidget;
QFormLayout *formLayout = new QFormLayout;
for (const auto &variable: m_document->variables()) {
auto name = variable.first;
auto value = valueOfKeyInMapOrEmpty(variable.second, "value").toFloat();
qDebug() << "Script variable, name:" << name << "value:" << value;
QDoubleSpinBox *inputBox = new QDoubleSpinBox;
inputBox->setValue(value);
connect(inputBox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, [=](double toValue) {
emit updateVariableValue(name, QString::number(toValue));
});
formLayout->addRow(name, inputBox);
}
widget->setLayout(formLayout);
setWidget(widget);
}
QSize ScriptVariablesWidget::sizeHint() const
{
return QSize(Theme::sidebarPreferredWidth, 0);
}

View File

@ -0,0 +1,25 @@
#ifndef DUST3D_SCRIPT_VARIABLES_WIDGET_H
#define DUST3D_SCRIPT_VARIABLES_WIDGET_H
#include <QWidget>
#include <QString>
#include <map>
#include <QScrollArea>
#include "document.h"
class ScriptVariablesWidget : public QScrollArea
{
Q_OBJECT
signals:
void updateVariableValue(const QString &name, const QString &value);
public:
ScriptVariablesWidget(const Document *document,
QWidget *parent=nullptr);
public slots:
void reload();
protected:
QSize sizeHint() const override;
private:
const Document *m_document = nullptr;
};
#endif

64
src/scriptwidget.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <QVBoxLayout>
#include <QSplitter>
#include "scriptwidget.h"
#include "scripteditwidget.h"
#include "theme.h"
ScriptWidget::ScriptWidget(const Document *document, QWidget *parent) :
QWidget(parent),
m_document(document)
{
m_consoleEdit = new QPlainTextEdit;
m_consoleEdit->setReadOnly(true);
m_consoleEdit->hide();
ScriptVariablesWidget *scriptVariablesWidget = new ScriptVariablesWidget(m_document);
scriptVariablesWidget->hide();
connect(m_document, &Document::mergedVaraiblesChanged, this, [=]() {
if (m_document->variables().empty())
scriptVariablesWidget->hide();
else
scriptVariablesWidget->show();
});
ScriptEditWidget *scriptEditWidget = new ScriptEditWidget;
connect(m_document, &Document::cleanupScript, scriptEditWidget, &ScriptEditWidget::clear);
connect(m_document, &Document::scriptModifiedFromExternal, this, [=]() {
scriptEditWidget->setPlainText(document->script());
});
connect(m_document, &Document::scriptErrorChanged, this, &ScriptWidget::updateScriptConsole);
connect(m_document, &Document::scriptConsoleLogChanged, this, &ScriptWidget::updateScriptConsole);
connect(scriptEditWidget, &ScriptEditWidget::scriptChanged, m_document, &Document::updateScript);
connect(m_document, &Document::mergedVaraiblesChanged, scriptVariablesWidget, &ScriptVariablesWidget::reload);
QSplitter *splitter = new QSplitter;
splitter->setOrientation(Qt::Vertical);
splitter->addWidget(scriptEditWidget);
splitter->addWidget(m_consoleEdit);
splitter->addWidget(scriptVariablesWidget);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(splitter);
setLayout(mainLayout);
}
QSize ScriptWidget::sizeHint() const
{
return QSize(Theme::sidebarPreferredWidth, 0);
}
void ScriptWidget::updateScriptConsole()
{
const auto &scriptError = m_document->scriptError();
const auto &scriptConsoleLog = m_document->scriptConsoleLog();
if (scriptError.isEmpty() && scriptConsoleLog.isEmpty()) {
m_consoleEdit->hide();
} else {
m_consoleEdit->setPlainText(scriptError + scriptConsoleLog);
m_consoleEdit->show();
}
}

22
src/scriptwidget.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef SCRIPT_WIDGET_H
#define SCRIPT_WIDGET_H
#include <QWidget>
#include <QPlainTextEdit>
#include "document.h"
#include "scriptvariableswidget.h"
class ScriptWidget : public QWidget
{
Q_OBJECT
public:
ScriptWidget(const Document *document, QWidget *parent=nullptr);
public slots:
void updateScriptConsole();
protected:
QSize sizeHint() const override;
private:
const Document *m_document = nullptr;
QPlainTextEdit *m_consoleEdit = nullptr;
};
#endif

64
src/variablesxml.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <QDebug>
#include "variablesxml.h"
void saveVariablesToXmlStream(const std::map<QString, std::map<QString, QString>> &variables, QXmlStreamWriter *writer)
{
writer->setAutoFormatting(true);
writer->writeStartDocument();
writer->writeStartElement("variables");
for (const auto &it: variables) {
writer->writeStartElement("variable");
writer->writeAttribute("name", it.first);
for (const auto &subIt: it.second) {
if (subIt.first == "name")
continue;
writer->writeAttribute(subIt.first, subIt.second);
}
writer->writeEndElement();
}
writer->writeEndElement();
writer->writeEndDocument();
}
void loadVariablesFromXmlStream(std::map<QString, std::map<QString, QString>> *variables, QXmlStreamReader &reader)
{
std::vector<QString> elementNameStack;
while (!reader.atEnd()) {
reader.readNext();
if (!reader.isStartElement() && !reader.isEndElement()) {
if (!reader.name().toString().isEmpty())
qDebug() << "Skip xml element:" << reader.name().toString() << " tokenType:" << reader.tokenType();
continue;
}
QString baseName = reader.name().toString();
if (reader.isStartElement())
elementNameStack.push_back(baseName);
QStringList nameItems;
for (const auto &nameItem: elementNameStack) {
nameItems.append(nameItem);
}
QString fullName = nameItems.join(".");
if (reader.isEndElement())
elementNameStack.pop_back();
if (reader.isStartElement()) {
if (fullName == "variables.variable") {
QString variableName;
std::map<QString, QString> map;
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
auto attrNameString = attr.name().toString();
auto attrValueString = attr.value().toString();
if ("name" == attrNameString) {
variableName = attrValueString;
} else {
map[attrNameString] = attrValueString;
}
}
if (!variableName.isEmpty()) {
(*variables)[variableName] = map;
}
}
}
}
}

9
src/variablesxml.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef DUST3D_VARIABLES_XML_H
#define DUST3D_VARIABLES_XML_H
#include <QXmlStreamWriter>
#include <map>
void saveVariablesToXmlStream(const std::map<QString, std::map<QString, QString>> &variables, QXmlStreamWriter *writer);
void loadVariablesFromXmlStream(std::map<QString, std::map<QString, QString>> *variables, QXmlStreamReader &reader);
#endif

View File

@ -0,0 +1,463 @@
#
# QuickJS Javascript Engine
#
# Copyright (c) 2017-2019 Fabrice Bellard
# Copyright (c) 2017-2019 Charlie Gordon
#
# 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.
ifeq ($(shell uname -s),Darwin)
CONFIG_DARWIN=y
endif
# Windows cross compilation from Linux
#CONFIG_WIN32=y
# use link time optimization (smaller and faster executables but slower build)
CONFIG_LTO=y
# consider warnings as errors (for development)
#CONFIG_WERROR=y
ifndef CONFIG_WIN32
# force 32 bit build for some utilities
CONFIG_M32=y
endif
ifdef CONFIG_DARWIN
# use clang instead of gcc
CONFIG_CLANG=y
CONFIG_DEFAULT_AR=y
endif
# installation directory
prefix=/usr/local
# use the gprof profiler
#CONFIG_PROFILE=y
# use address sanitizer
#CONFIG_ASAN=y
OBJDIR=.obj
ifdef CONFIG_WIN32
CROSS_PREFIX=i686-w64-mingw32-
EXE=.exe
else
CROSS_PREFIX=
EXE=
endif
ifdef CONFIG_CLANG
CC=$(CROSS_PREFIX)clang
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS += -Wextra
CFLAGS += -Wno-sign-compare
CFLAGS += -Wno-missing-field-initializers
CFLAGS += -Wundef -Wuninitialized
CFLAGS += -Wunused -Wno-unused-parameter
CFLAGS += -Wwrite-strings
CFLAGS += -Wchar-subscripts -funsigned-char
CFLAGS += -MMD -MF $(OBJDIR)/$(@F).d
ifdef CONFIG_DEFAULT_AR
AR=$(CROSS_PREFIX)ar
else
ifdef CONFIG_LTO
AR=$(CROSS_PREFIX)llvm-ar
else
AR=$(CROSS_PREFIX)ar
endif
endif
else
CC=$(CROSS_PREFIX)gcc
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS += -Wno-array-bounds
ifdef CONFIG_LTO
AR=$(CROSS_PREFIX)gcc-ar
else
AR=$(CROSS_PREFIX)ar
endif
endif
ifdef CONFIG_WERROR
CFLAGS+=-Werror
endif
DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
CFLAGS+=$(DEFINES)
CFLAGS_DEBUG=$(CFLAGS) -O0
CFLAGS_SMALL=$(CFLAGS) -Os
CFLAGS_OPT=$(CFLAGS) -O2
CFLAGS_NOLTO:=$(CFLAGS_OPT)
LDFLAGS=-g
ifdef CONFIG_LTO
CFLAGS_SMALL+=-flto
CFLAGS_OPT+=-flto
LDFLAGS+=-flto
endif
ifdef CONFIG_PROFILE
CFLAGS+=-p
LDFLAGS+=-p
endif
ifdef CONFIG_ASAN
CFLAGS+=-fsanitize=address
LDFLAGS+=-fsanitize=address
endif
ifdef CONFIG_WIN32
LDEXPORT=
else
LDEXPORT=-rdynamic
endif
PROGS=qjs$(EXE) qjsbn$(EXE) qjsc qjsbnc run-test262 run-test262-bn
ifndef CONFIG_WIN32
PROGS+=qjscalc
endif
ifdef CONFIG_M32
PROGS+=qjs32 qjs32_s qjsbn32
endif
PROGS+=libquickjs.a libquickjs.bn.a
ifdef CONFIG_LTO
PROGS+=libquickjs.lto.a libquickjs.bn.lto.a
endif
# examples
ifdef CONFIG_ASAN
PROGS+=
else ifdef CONFIG_WIN32
PROGS+=
else
PROGS+=examples/hello examples/hello_module examples/c_module
endif
all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS)
QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o
QJSBN_LIB_OBJS=$(patsubst %.o, %.bn.o, $(QJS_LIB_OBJS)) $(OBJDIR)/libbf.bn.o
QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
QJSBN_OBJS=$(OBJDIR)/qjs.bn.o $(OBJDIR)/repl-bn.bn.o $(OBJDIR)/qjscalc.bn.o $(QJSBN_LIB_OBJS)
LIBS=-lm
ifndef CONFIG_WIN32
LIBS+=-ldl
endif
$(OBJDIR):
mkdir -p $(OBJDIR)
qjs$(EXE): $(QJS_OBJS)
$(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
qjsc: $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
qjsbnc: $(OBJDIR)/qjsc.bn.o $(QJSBN_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
QJSC_DEFINES:=-DCONFIG_CC=\"$(CC)\"
ifdef CONFIG_LTO
QJSC_DEFINES+=-DCONFIG_LTO
endif
QJSC_DEFINES+=-DCONFIG_PREFIX=\"$(prefix)\"
$(OBJDIR)/qjsc.o $(OBJDIR)/qjsc.bn.o: CFLAGS+=$(QJSC_DEFINES)
qjs32: $(patsubst %.o, %.m32.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
@size $@
qjsbn$(EXE): $(QJSBN_OBJS)
$(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjsbn32: $(patsubst %.o, %.m32.o, $(QJSBN_OBJS))
$(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjscalc: qjsbn
ln -sf $< $@
qjsbn-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJSBN_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
ifdef CONFIG_LTO
LTOEXT=.lto
else
LTOEXT=
endif
libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS)
$(AR) rcs $@ $^
libquickjs.bn$(LTOEXT).a: $(QJSBN_LIB_OBJS)
$(AR) rcs $@ $^
ifdef CONFIG_LTO
libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^
libquickjs.bn.a: $(patsubst %.o, %.nolto.o, $(QJSBN_LIB_OBJS))
$(AR) rcs $@ $^
endif # CONFIG_LTO
repl.c: qjsc repl.js
./qjsc -c -o $@ -m repl.js
repl-bn.c: qjsbnc repl.js
./qjsbnc -c -o $@ -m repl.js
qjscalc.c: qjsbnc qjscalc.js
./qjsbnc -c -o $@ qjscalc.js
ifneq ($(wildcard unicode/UnicodeData.txt),)
$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o $(OBJDIR)/libunicode.bn.o \
$(OBJDIR)/libunicode.nolto.o $(OBJDIR)/libunicode.bn.nolto.o: libunicode-table.h
libunicode-table.h: unicode_gen
./unicode_gen unicode $@
endif
run-test262: $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread
run-test262-bn: $(OBJDIR)/run-test262.bn.o $(QJSBN_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread
run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread
run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread
run-test262-bn32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.bn.o $(QJSBN_LIB_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread
# object suffix order: bn, nolto, [m32|m32s]
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.pic.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $<
$(OBJDIR)/%.bn.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -DCONFIG_BIGNUM -c -o $@ $<
$(OBJDIR)/%.nolto.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_NOLTO) -c -o $@ $<
$(OBJDIR)/%.bn.nolto.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_NOLTO) -DCONFIG_BIGNUM -c -o $@ $<
$(OBJDIR)/%.m32.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.m32s.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_SMALL) -c -o $@ $<
$(OBJDIR)/%.bn.m32.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_OPT) -DCONFIG_BIGNUM -c -o $@ $<
$(OBJDIR)/%.debug.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_DEBUG) -c -o $@ $<
$(OBJDIR)/%.bn.debug.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_DEBUG) -DCONFIG_BIGNUM -c -o $@ $<
$(OBJDIR)/%.check.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $<
regexp_test: libregexp.c libunicode.c cutils.c
$(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS)
jscompress: jscompress.c
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ jscompress.c
unicode_gen: unicode_gen.c cutils.c libunicode.c
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ unicode_gen.c cutils.c
clean:
rm -f repl.c repl-bn.c qjscalc.c out.c
rm -f *.a *.so *.o *.d *~ jscompress unicode_gen regexp_test $(PROGS)
rm -f hello.c hello_module.c c_module.c
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug qjsbn-debug
rm -rf run-test262-debug run-test262-32 run-test262-bn32
install: all
mkdir -p "$(prefix)/bin"
install -m755 -s qjs qjsc qjsbn qjsbnc "$(prefix)/bin"
ln -sf qjsbn "$(prefix)/bin/qjscalc"
mkdir -p "$(prefix)/lib/quickjs"
install -m755 libquickjs.a libquickjs.bn.a "$(prefix)/lib/quickjs"
ifdef CONFIG_LTO
install -m755 libquickjs.lto.a libquickjs.bn.lto.a "$(prefix)/lib/quickjs"
endif
mkdir -p "$(prefix)/include/quickjs"
install -m755 quickjs.h quickjs-libc.h "$(prefix)/include/quickjs"
###############################################################################
# examples
# example of static JS compilation
HELLO_SRCS=examples/hello.js
HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy
hello.c: qjsc $(HELLO_SRCS)
./qjsc -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS)
ifdef CONFIG_M32
examples/hello: $(OBJDIR)/hello.m32s.o $(patsubst %.o, %.m32s.o, $(QJS_LIB_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
else
examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
endif
# example of static JS compilation with modules
HELLO_MODULE_SRCS=examples/hello_module.js
HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy -m
examples/hello_module: qjsc libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS)
./qjsc $(HELLO_MODULE_OPTS) -o $@ $(HELLO_MODULE_SRCS)
# use of an external C module (static compilation)
c_module.c: qjsc examples/c_module.js
./qjsc -e -M examples/fib.so,fib -m -o $@ examples/c_module.js
examples/c_module: $(OBJDIR)/c_module.o $(OBJDIR)/fib.o libquickjs$(LTOEXT).a
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
$(OBJDIR)/fib.o: examples/fib.c
$(CC) $(CFLAGS_OPT) -c -o $@ $<
###############################################################################
# documentation
DOCS=doc/quickjs.pdf doc/quickjs.html doc/jsbignum.pdf doc/jsbignum.html
build_doc: $(DOCS)
clean_doc:
rm -f $(DOCS)
doc/%.pdf: doc/%.texi
texi2pdf --clean -o $@ -q $<
doc/%.html: doc/%.texi
makeinfo --html --no-headers --no-split --number-sections -o $@ $<
###############################################################################
# tests
ifndef CONFIG_DARWIN
test: bjson.so
endif
test: qjs qjsbn
./qjs tests/test_closure.js
./qjs tests/test_op.js
./qjs tests/test_builtin.js
./qjs tests/test_loop.js
./qjs -m tests/test_std.js
ifndef CONFIG_DARWIN
./qjs -m tests/test_bjson.js
endif
./qjsbn tests/test_closure.js
./qjsbn tests/test_op.js
./qjsbn tests/test_builtin.js
./qjsbn tests/test_loop.js
./qjsbn -m tests/test_std.js
./qjsbn --qjscalc tests/test_bignum.js
test-32: qjs32 qjsbn32
./qjs32 tests/test_closure.js
./qjs32 tests/test_op.js
./qjs32 tests/test_builtin.js
./qjs32 tests/test_loop.js
./qjs32 -m tests/test_std.js
./qjsbn32 tests/test_closure.js
./qjsbn32 tests/test_op.js
./qjsbn32 tests/test_builtin.js
./qjsbn32 tests/test_loop.js
./qjsbn32 -m tests/test_std.js
./qjsbn32 --qjscalc tests/test_bignum.js
stats: qjs qjs32
./qjs -qd
./qjs32 -qd
microbench: qjs
./qjs tests/microbench.js
microbench-32: qjs32
./qjs32 tests/microbench.js
# ES5 tests (obsolete)
test2o: run-test262
time ./run-test262 -m -c test262o.conf
test2o-32: run-test262-32
time ./run-test262-32 -m -c test262o.conf
test2o-update: run-test262
./run-test262 -u -c test262o.conf
# Test262 tests
test2-default: run-test262
time ./run-test262 -m -c test262.conf
test2: run-test262
time ./run-test262 -m -c test262.conf -a
test2-32: run-test262-32
time ./run-test262-32 -m -c test262.conf -a
test2-update: run-test262
./run-test262 -u -c test262.conf -a
test2-check: run-test262
time ./run-test262 -m -c test262.conf -E -a
# Test262 + BigInt tests
test2bn-default: run-test262-bn
time ./run-test262-bn -m -c test262bn.conf
test2bn: run-test262-bn
time ./run-test262-bn -m -c test262bn.conf -a
test2bn-32: run-test262-bn32
time ./run-test262-bn32 -m -c test262bn.conf -a
testall: all test microbench test2o test2 test2bn
testall-32: all test-32 microbench-32 test2o-32 test2-32 test2bn-32
testall-complete: testall testall-32
bench-v8: qjs qjs32
make -C tests/bench-v8
./qjs -d tests/bench-v8/combined.js
bjson.so: $(OBJDIR)/bjson.pic.o
$(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS)
-include $(wildcard $(OBJDIR)/*.d)

View File

@ -0,0 +1,83 @@
- 64-bit atoms in 64-bit mode?
- rename CONFIG_ALL_UNICODE, CONFIG_BIGNUM, CONFIG_ATOMICS, CONFIG_CHECK_JSVALUE ?
- unify coding style and naming conventions
- use names from the ECMA spec in library implementation
- modules: if no ".", use a well known module loading path ?
- use JSHoistedDef only for global variables (JSHoistedDef.var_name != JS_ATOM_NULL)
- add index in JSVarDef and is_arg flag to merge args and vars in JSFunctionDef
- replace most JSVarDef flags with var_type enumeration
- use byte code emitters with typed arguments (for clarity)
- use 2 bytecode DynBufs in JSFunctionDef, one for reading, one for writing
and use the same wrappers in all phases
- use more generic method for line numbers in resolve_variables and resolve_labels
- bignum:
- fix div/pow div by zero exception in doc & code in bigint mode
- fix Atomics support
Memory:
- test border cases for max number of atoms, object properties, string length
- add emergency malloc mode for out of memory exceptions.
- test all DynBuf memory errors
- test all js_realloc memory errors
- bignum: handle memory errors
- use memory pools for objects, etc?
- improve JS_ComputeMemoryUsage() with more info
Optimizations:
- use auto-init properties for more global objects
- reuse stack slots for disjoint scopes, if strip
- optimize `for of` iterator for built-in array objects
- add heuristic to avoid some cycles in closures
- small String (0-2 charcodes) with immediate storage
- perform static string concatenation at compile time
- optimize string concatenation with ropes or miniropes?
- add implicit numeric strings for Uint32 numbers?
- optimize `s += a + b`, `s += a.b` and similar simple expressions
- ensure string canonical representation and optimise comparisons and hashes?
- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references
- optimize function storage with length and name accessors?
- property access optimization on the global object, functions,
prototypes and special non extensible objects.
- create object literals with the correct length by backpatching length argument
- remove redundant set_loc_uninitialized/check_uninitialized opcodes
- peephole optim: push_atom_value, to_propkey -> push_atom_value
- peephole optim: put_loc x, get_loc_check x -> set_loc x
- comparative performance benchmark
- use variable name when throwing uninitialized exception if available
- convert slow array to fast array when all properties != length are numeric
- optimize destructuring assignments for global and local variables
- implement some form of tail-call-optimization
- debugger keyword support
- optmize OP_apply
- optimize f(...b)
Extensions:
- support more features in [features] section
- add TC39 stage 3 proposals: String.prototype.matchAll, Symbol.matchAll
- fix preprocessor bug if nested #ifdef in jscompress.c
- add built-in preprocessor in compiler, get rid of jscompress
handle #if, #ifdef, #line, limited support for #define
- limited support for web assembly
- get rid of __loadScript, use more common name
- BSD sockets
- Process or thread control
- use custom printf to avoid C library compatibility issues
- use custom timezone support to avoid C library compatibility issues
REPL:
- strip internal functions from stack trace
- readline: support MS Windows terminal
- readline: handle dynamic terminal resizing
- multiline editing
- debugger
- runtime object and function inspectors
- interactive object browser
- use more generic approach to display evaluation results
- improve directive handling: dispatch, colorize, completion...
- save history
- close all predefined methods in repl.js and jscalc.js
Test262o: 0/11266 errors, 459 excluded
Test262: 0/55855 errors, 504 excluded, 1559 skipped
Test262bn: 0/57176 errors, 724 excluded, 675 skipped
test262 commit: 94b1e80ab3440413df916cd56d29c5a2fa2ac451

View File

@ -0,0 +1 @@
2019-07-09

View File

@ -0,0 +1,88 @@
/*
* QuickJS: binary JSON module (test only)
*
* Copyright (c) 2017-2019 Fabrice Bellard
*
* 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.
*/
#include "quickjs-libc.h"
#include "cutils.h"
static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
uint8_t *buf;
uint64_t pos, len;
JSValue obj;
size_t size;
if (JS_ToIndex(ctx, &pos, argv[1]))
return JS_EXCEPTION;
if (JS_ToIndex(ctx, &len, argv[2]))
return JS_EXCEPTION;
buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
if (!buf)
return JS_EXCEPTION;
if (pos + len > size)
return JS_ThrowRangeError(ctx, "array buffer overflow");
obj = JS_ReadObject(ctx, buf + pos, len, 0);
return obj;
}
static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
size_t len;
uint8_t *buf;
JSValue array;
buf = JS_WriteObject(ctx, &len, argv[0], 0);
if (!buf)
return JS_EXCEPTION;
array = JS_NewArrayBufferCopy(ctx, buf, len);
js_free(ctx, buf);
return array;
}
static const JSCFunctionListEntry js_bjson_funcs[] = {
JS_CFUNC_DEF("read", 3, js_bjson_read ),
JS_CFUNC_DEF("write", 1, js_bjson_write ),
};
static int js_bjson_init(JSContext *ctx, JSModuleDef *m)
{
return JS_SetModuleExportList(ctx, m, js_bjson_funcs,
countof(js_bjson_funcs));
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_bjson
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_bjson_init);
if (!m)
return NULL;
JS_AddModuleExportList(ctx, m, js_bjson_funcs, countof(js_bjson_funcs));
return m;
}

View File

@ -0,0 +1,621 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "cutils.h"
void pstrcpy(char *buf, int buf_size, const char *str)
{
int c;
char *q = buf;
if (buf_size <= 0)
return;
for(;;) {
c = *str++;
if (c == 0 || q >= buf + buf_size - 1)
break;
*q++ = c;
}
*q = '\0';
}
/* strcat and truncate. */
char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
int strstart(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = p;
return 1;
}
int has_suffix(const char *str, const char *suffix)
{
size_t len = strlen(str);
size_t slen = strlen(suffix);
return (len >= slen && !memcmp(str + len - slen, suffix, slen));
}
/* Dynamic buffer package */
static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)
{
return realloc(ptr, size);
}
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)
{
memset(s, 0, sizeof(*s));
if (!realloc_func)
realloc_func = dbuf_default_realloc;
s->opaque = opaque;
s->realloc_func = realloc_func;
}
void dbuf_init(DynBuf *s)
{
dbuf_init2(s, NULL, NULL);
}
/* return < 0 if error */
int dbuf_realloc(DynBuf *s, size_t new_size)
{
size_t size;
uint8_t *new_buf;
if (new_size > s->allocated_size) {
if (s->error)
return -1;
size = s->allocated_size * 3 / 2;
if (size > new_size)
new_size = size;
new_buf = s->realloc_func(s->opaque, s->buf, new_size);
if (!new_buf) {
s->error = TRUE;
return -1;
}
s->buf = new_buf;
s->allocated_size = new_size;
}
return 0;
}
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
{
size_t end;
end = offset + len;
if (dbuf_realloc(s, end))
return -1;
memcpy(s->buf + offset, data, len);
if (end > s->size)
s->size = end;
return 0;
}
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
{
if (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len))
return -1;
}
memcpy(s->buf + s->size, data, len);
s->size += len;
return 0;
}
int dbuf_put_self(DynBuf *s, size_t offset, size_t len)
{
if (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len))
return -1;
}
memcpy(s->buf + s->size, s->buf + offset, len);
s->size += len;
return 0;
}
int dbuf_putc(DynBuf *s, uint8_t c)
{
return dbuf_put(s, &c, 1);
}
int dbuf_putstr(DynBuf *s, const char *str)
{
return dbuf_put(s, (const uint8_t *)str, strlen(str));
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...)
{
va_list ap;
char buf[128];
int len;
va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (len < sizeof(buf)) {
/* fast case */
return dbuf_put(s, (uint8_t *)buf, len);
} else {
if (dbuf_realloc(s, s->size + len + 1))
return -1;
va_start(ap, fmt);
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
fmt, ap);
va_end(ap);
s->size += len;
}
return 0;
}
void dbuf_free(DynBuf *s)
{
/* we test s->buf as a fail safe to avoid crashing if dbuf_free()
is called twice */
if (s->buf) {
s->realloc_func(s->opaque, s->buf, 0);
}
memset(s, 0, sizeof(*s));
}
/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
are output. */
int unicode_to_utf8(uint8_t *buf, unsigned int c)
{
uint8_t *q = buf;
if (c < 0x80) {
*q++ = c;
} else {
if (c < 0x800) {
*q++ = (c >> 6) | 0xc0;
} else {
if (c < 0x10000) {
*q++ = (c >> 12) | 0xe0;
} else {
if (c < 0x00200000) {
*q++ = (c >> 18) | 0xf0;
} else {
if (c < 0x04000000) {
*q++ = (c >> 24) | 0xf8;
} else if (c < 0x80000000) {
*q++ = (c >> 30) | 0xfc;
*q++ = ((c >> 24) & 0x3f) | 0x80;
} else {
return 0;
}
*q++ = ((c >> 18) & 0x3f) | 0x80;
}
*q++ = ((c >> 12) & 0x3f) | 0x80;
}
*q++ = ((c >> 6) & 0x3f) | 0x80;
}
*q++ = (c & 0x3f) | 0x80;
}
return q - buf;
}
static const unsigned int utf8_min_code[5] = {
0x80, 0x800, 0x10000, 0x00200000, 0x04000000,
};
static const unsigned char utf8_first_code_mask[5] = {
0x1f, 0xf, 0x7, 0x3, 0x1,
};
/* return -1 if error. *pp is not updated in this case. max_len must
be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
{
int l, c, b, i;
c = *p++;
if (c < 0x80) {
*pp = p;
return c;
}
switch(c) {
case 0xc0 ... 0xdf:
l = 1;
break;
case 0xe0 ... 0xef:
l = 2;
break;
case 0xf0 ... 0xf7:
l = 3;
break;
case 0xf8 ... 0xfb:
l = 4;
break;
case 0xfc ... 0xfd:
l = 5;
break;
default:
return -1;
}
/* check that we have enough characters */
if (l > (max_len - 1))
return -1;
c &= utf8_first_code_mask[l - 1];
for(i = 0; i < l; i++) {
b = *p++;
if (b < 0x80 || b >= 0xc0)
return -1;
c = (c << 6) | (b & 0x3f);
}
if (c < utf8_min_code[l - 1])
return -1;
*pp = p;
return c;
}
#if 0
#if defined(EMSCRIPTEN) || defined(__ANDROID__)
static void *rqsort_arg;
static int (*rqsort_cmp)(const void *, const void *, void *);
static int rqsort_cmp2(const void *p1, const void *p2)
{
return rqsort_cmp(p1, p2, rqsort_arg);
}
/* not reentrant, but not needed with emscripten */
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg)
{
rqsort_arg = arg;
rqsort_cmp = cmp;
qsort(base, nmemb, size, rqsort_cmp2);
}
#endif
#else
typedef void (*exchange_f)(void *a, void *b, size_t size);
typedef int (*cmp_f)(const void *, const void *, void *opaque);
static void exchange_bytes(void *a, void *b, size_t size) {
uint8_t *ap = (uint8_t *)a;
uint8_t *bp = (uint8_t *)b;
while (size-- != 0) {
uint8_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_byte(void *a, void *b, size_t size) {
uint8_t *ap = (uint8_t *)a;
uint8_t *bp = (uint8_t *)b;
uint8_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int16s(void *a, void *b, size_t size) {
uint16_t *ap = (uint16_t *)a;
uint16_t *bp = (uint16_t *)b;
for (size /= sizeof(uint16_t); size-- != 0;) {
uint16_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int16(void *a, void *b, size_t size) {
uint16_t *ap = (uint16_t *)a;
uint16_t *bp = (uint16_t *)b;
uint16_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int32s(void *a, void *b, size_t size) {
uint32_t *ap = (uint32_t *)a;
uint32_t *bp = (uint32_t *)b;
for (size /= sizeof(uint32_t); size-- != 0;) {
uint32_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int32(void *a, void *b, size_t size) {
uint32_t *ap = (uint32_t *)a;
uint32_t *bp = (uint32_t *)b;
uint32_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int64s(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
for (size /= sizeof(uint64_t); size-- != 0;) {
uint64_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int64(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
uint64_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int128s(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) {
uint64_t t = ap[0];
uint64_t u = ap[1];
ap[0] = bp[0];
ap[1] = bp[1];
bp[0] = t;
bp[1] = u;
}
}
static void exchange_one_int128(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
uint64_t t = ap[0];
uint64_t u = ap[1];
ap[0] = bp[0];
ap[1] = bp[1];
bp[0] = t;
bp[1] = u;
}
static inline exchange_f exchange_func(const void *base, size_t size) {
switch (((uintptr_t)base | (uintptr_t)size) & 15) {
case 0:
if (size == sizeof(uint64_t) * 2)
return exchange_one_int128;
else
return exchange_int128s;
case 8:
if (size == sizeof(uint64_t))
return exchange_one_int64;
else
return exchange_int64s;
case 4:
case 12:
if (size == sizeof(uint32_t))
return exchange_one_int32;
else
return exchange_int32s;
case 2:
case 6:
case 10:
case 14:
if (size == sizeof(uint16_t))
return exchange_one_int16;
else
return exchange_int16s;
default:
if (size == 1)
return exchange_one_byte;
else
return exchange_bytes;
}
}
static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
{
uint8_t *basep = (uint8_t *)base;
size_t i, n, c, r;
exchange_f swap = exchange_func(base, size);
if (nmemb > 1) {
i = (nmemb / 2) * size;
n = nmemb * size;
while (i > 0) {
i -= size;
for (r = i; (c = r * 2 + size) < n; r = c) {
if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0)
c += size;
if (cmp(basep + r, basep + c, opaque) > 0)
break;
swap(basep + r, basep + c, size);
}
}
for (i = n - size; i > 0; i -= size) {
swap(basep, basep + i, size);
for (r = 0; (c = r * 2 + size) < i; r = c) {
if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0)
c += size;
if (cmp(basep + r, basep + c, opaque) > 0)
break;
swap(basep + r, basep + c, size);
}
}
}
}
static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque)
{
return cmp(a, b, opaque) < 0 ?
(cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) :
(cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c ));
}
/* pointer based version with local stack and insertion sort threshhold */
void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
{
struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack;
uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m;
size_t m4, i, lt, gt, span, span2;
int c, depth;
exchange_f swap = exchange_func(base, size);
exchange_f swap_block = exchange_func(base, size | 128);
if (nmemb < 2 || size <= 0)
return;
sp->base = (uint8_t *)base;
sp->count = nmemb;
sp->depth = 0;
sp++;
while (sp > stack) {
sp--;
ptr = sp->base;
nmemb = sp->count;
depth = sp->depth;
while (nmemb > 6) {
if (++depth > 50) {
/* depth check to ensure worst case logarithmic time */
heapsortx(ptr, nmemb, size, cmp, opaque);
nmemb = 0;
break;
}
/* select median of 3 from 1/4, 1/2, 3/4 positions */
/* should use median of 5 or 9? */
m4 = (nmemb >> 2) * size;
m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque);
swap(ptr, m, size); /* move the pivot to the start or the array */
i = lt = 1;
pi = plt = ptr + size;
gt = nmemb;
pj = pgt = top = ptr + nmemb * size;
for (;;) {
while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) {
if (c == 0) {
swap(plt, pi, size);
lt++;
plt += size;
}
i++;
pi += size;
}
while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) {
if (c == 0) {
gt--;
pgt -= size;
swap(pgt, pj, size);
}
}
if (pi >= pj)
break;
swap(pi, pj, size);
i++;
pi += size;
}
/* array has 4 parts:
* from 0 to lt excluded: elements identical to pivot
* from lt to pi excluded: elements smaller than pivot
* from pi to gt excluded: elements greater than pivot
* from gt to n excluded: elements identical to pivot
*/
/* move elements identical to pivot in the middle of the array: */
/* swap values in ranges [0..lt[ and [i-lt..i[
swapping the smallest span between lt and i-lt is sufficient
*/
span = plt - ptr;
span2 = pi - plt;
plt = pi - span;
lt = i - lt;
if (span > span2)
span = span2;
swap_block(ptr, pi - span, span);
/* swap values in ranges [gt..top[ and [i..top-(top-gt)[
swapping the smallest span between top-gt and gt-i is sufficient
*/
span = top - pgt;
span2 = pgt - pi;
pgt = top - span2;
gt = nmemb - (gt - i);
if (span > span2)
span = span2;
swap_block(pi, top - span, span);
/* now array has 3 parts:
* from 0 to lt excluded: elements smaller than pivot
* from lt to gt excluded: elements identical to pivot
* from gt to n excluded: elements greater than pivot
*/
/* stack the larger segment and keep processing the smaller one
to minimize stack use for pathological distributions */
if (lt > nmemb - gt) {
sp->base = ptr;
sp->count = lt;
sp->depth = depth;
sp++;
ptr = pgt;
nmemb -= gt;
} else {
sp->base = pgt;
sp->count = nmemb - gt;
sp->depth = depth;
sp++;
nmemb = lt;
}
}
/* Use insertion sort for small fragments */
for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) {
for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size)
swap(pj, pj - size, size);
}
}
}
#endif

View File

@ -0,0 +1,292 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* 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.
*/
#ifndef CUTILS_H
#define CUTILS_H
#include <stdlib.h>
#include <inttypes.h>
/* set if CPU is big endian */
#undef WORDS_BIGENDIAN
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define force_inline inline __attribute__((always_inline))
#define no_inline __attribute__((noinline))
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#ifndef countof
#define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif
typedef int BOOL;
#ifndef FALSE
enum {
FALSE = 0,
TRUE = 1,
};
#endif
void pstrcpy(char *buf, int buf_size, const char *str);
char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
int has_suffix(const char *str, const char *suffix);
static inline int max_int(int a, int b)
{
if (a > b)
return a;
else
return b;
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
else
return b;
}
static inline uint32_t max_uint32(uint32_t a, uint32_t b)
{
if (a > b)
return a;
else
return b;
}
static inline uint32_t min_uint32(uint32_t a, uint32_t b)
{
if (a < b)
return a;
else
return b;
}
static inline int64_t max_int64(int64_t a, int64_t b)
{
if (a > b)
return a;
else
return b;
}
static inline int64_t min_int64(int64_t a, int64_t b)
{
if (a < b)
return a;
else
return b;
}
/* WARNING: undefined if a = 0 */
static inline int clz32(unsigned int a)
{
return __builtin_clz(a);
}
/* WARNING: undefined if a = 0 */
static inline int clz64(uint64_t a)
{
return __builtin_clzll(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz32(unsigned int a)
{
return __builtin_ctz(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz64(uint64_t a)
{
return __builtin_ctzll(a);
}
struct __attribute__((packed)) packed_u64 {
uint64_t v;
};
struct __attribute__((packed)) packed_u32 {
uint32_t v;
};
struct __attribute__((packed)) packed_u16 {
uint16_t v;
};
static inline uint64_t get_u64(const uint8_t *tab)
{
return ((const struct packed_u64 *)tab)->v;
}
static inline int64_t get_i64(const uint8_t *tab)
{
return (int64_t)((const struct packed_u64 *)tab)->v;
}
static inline void put_u64(uint8_t *tab, uint64_t val)
{
((struct packed_u64 *)tab)->v = val;
}
static inline uint32_t get_u32(const uint8_t *tab)
{
return ((const struct packed_u32 *)tab)->v;
}
static inline int32_t get_i32(const uint8_t *tab)
{
return (int32_t)((const struct packed_u32 *)tab)->v;
}
static inline void put_u32(uint8_t *tab, uint32_t val)
{
((struct packed_u32 *)tab)->v = val;
}
static inline uint32_t get_u16(const uint8_t *tab)
{
return ((const struct packed_u16 *)tab)->v;
}
static inline int32_t get_i16(const uint8_t *tab)
{
return (int16_t)((const struct packed_u16 *)tab)->v;
}
static inline void put_u16(uint8_t *tab, uint16_t val)
{
((struct packed_u16 *)tab)->v = val;
}
static inline uint32_t get_u8(const uint8_t *tab)
{
return *tab;
}
static inline int32_t get_i8(const uint8_t *tab)
{
return (int8_t)*tab;
}
static inline void put_u8(uint8_t *tab, uint8_t val)
{
*tab = val;
}
static inline uint16_t bswap16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
static inline uint32_t bswap32(uint32_t v)
{
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
}
static inline uint64_t bswap64(uint64_t v)
{
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
}
/* XXX: should take an extra argument to pass slack information to the caller */
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
typedef struct DynBuf {
uint8_t *buf;
size_t size;
size_t allocated_size;
BOOL error; /* true if a memory allocation error occurred */
DynBufReallocFunc *realloc_func;
void *opaque; /* for realloc_func */
} DynBuf;
void dbuf_init(DynBuf *s);
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
int dbuf_realloc(DynBuf *s, size_t new_size);
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len);
int dbuf_put_self(DynBuf *s, size_t offset, size_t len);
int dbuf_putc(DynBuf *s, uint8_t c);
int dbuf_putstr(DynBuf *s, const char *str);
static inline int dbuf_put_u16(DynBuf *s, uint16_t val)
{
return dbuf_put(s, (uint8_t *)&val, 2);
}
static inline int dbuf_put_u32(DynBuf *s, uint32_t val)
{
return dbuf_put(s, (uint8_t *)&val, 4);
}
static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
{
return dbuf_put(s, (uint8_t *)&val, 8);
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...);
void dbuf_free(DynBuf *s);
static inline BOOL dbuf_error(DynBuf *s) {
return s->error;
}
#define UTF8_CHAR_LEN_MAX 6
int unicode_to_utf8(uint8_t *buf, unsigned int c);
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
static inline int from_hex(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
return -1;
}
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg);
#endif /* CUTILS_H */

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,844 @@
\input texinfo
@iftex
@afourpaper
@headings double
@end iftex
@titlepage
@afourpaper
@sp 7
@center @titlefont{Javascript Bignum Extensions}
@sp 3
@center Version 2018-06-16
@sp 3
@center Author: Fabrice Bellard
@end titlepage
@setfilename spec.info
@settitle Javascript Bignum Extensions
@contents
@chapter Introduction
The Bignum extensions add the following features to the Javascript
language while being 100% backward compatible:
@itemize
@item Overloading of the standard operators
to support new types such as complex numbers, fractions or matrixes.
@item Bigint mode where arbitrarily large integers are available by default (no @code{n} suffix is necessary as in the TC39 BigInt proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}}).
@item Arbitrarily large floating point numbers (@code{BigFloat}) in base 2 using the IEEE 754 semantics.
@item Optional @code{math} mode which modifies the semantics of the division, modulo and power operator. The division and power operator return a fraction with integer operands and the modulo operator is defined as the Euclidian remainder.
@end itemize
The extensions are independent from each other except the @code{math}
mode which relies on the bigint mode and the operator overloading.
@chapter Operator overloading
@section Introduction
If the operands of an operator have at least one object type, a custom
operator method is searched before doing the legacy Javascript
@code{ToNumber} conversion.
For unary operators, the custom function is looked up in the object
and has the following name:
@table @code
@item unary +
@code{Symbol.operatorPlus}
@item unary -
@code{Symbol.operatorNeg}
@item ++
@code{Symbol.operatorInc}
@item --
@code{Symbol.operatorDec}
@item ~
@code{Symbol.operatorNot}
@end table
For binary operators:
@itemize
@item
If both operands have the same constructor function, then the operator
is looked up in the constructor.
@item
Otherwise, the property @code{Symbol.operatorOrder} is looked up in both
constructors and converted to @code{Int32}. The operator is then
looked in the constructor with the larger @code{Symbol.operatorOrder}
value. A @code{TypeError} is raised if both constructors have the same
@code{Symbol.operatorOrder} value.
@end itemize
The operator is looked up with the following name:
@table @code
@item +
@code{Symbol.operatorAdd}
@item -
@code{Symbol.operatorSub}
@item *
@code{Symbol.operatorMul}
@item /
@code{Symbol.operatorDiv}
@item / (math mode)
@code{Symbol.operatorMathDiv}
@item %
@code{Symbol.operatorMod}
@item % (math mode)
@code{Symbol.operatorMathMod}
@item ^ (math mode)
@code{Symbol.operatorMathPow}
@item **
@code{Symbol.operatorPow}
@item |
@code{Symbol.operatorOr}
@item ^
@code{Symbol.operatorXor}
@item &
@code{Symbol.operatorAnd}
@item <<
@code{Symbol.operatorShl}
@item >>
@code{Symbol.operatorShr}
@item <
@code{Symbol.operatorCmpLT}
@item >
@code{Symbol.operatorCmpLT}, operands swapped
@item <=
@code{Symbol.operatorCmpLE}
@item >=
@code{Symbol.operatorCmpLE}, operands swapped
@item ==, !=
@code{Symbol.operatorCmpEQ}
@end table
The return value of @code{Symbol.operatorCmpLT}, @code{Symbol.operatorCmpLE} and
@code{Symbol.operatorCmpEQ} is converted to @code{Boolean}.
@section Builtin Object changes
@subsection @code{Symbol} constructor
The following global symbols are added for the operator overloading:
@table @code
@item operatorOrder
@item operatorAdd
@item operatorSub
@item operatorMul
@item operatorDiv
@item operatorMod
@item operatorPow
@item operatorShl
@item operatorShr
@item operatorAnd
@item operatorOr
@item operatorXor
@item operatorCmpLT
@item operatorCmpLE
@item operatorCmpEQ
@item operatorPlus
@item operatorNeg
@item operatorNot
@item operatorInc
@item operatorDec
@end table
@chapter The BigInt Mode
@section Introduction
The bigint mode is enabled with the @code{"use bigint"} directive. It
propagates the same way as the strict mode. In bigint mode, all
integers are considered as @code{bigint} (arbitrarily large integer,
similar to the TC39 BigInt
proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}})
instead of @code{number} (floating point number). In order to be able
to exchange data between standard and bigint modes, numbers are
internally represented as 3 different types:
@itemize
@item Small integer (SmallInt): 32 bit integer@footnote{Could be extended to 53 bits without changing the principle.}.
@item Big integer (BigInt): arbitrarily large integer.
@item Floating point number (Float).
@end itemize
In standard mode, the semantics of each operation is modified so that
when it returns a @code{number}, it is either of SmallInt or
Float. But the difference between SmallInt and Float is not observable
in standard mode.
In bigint mode, each operation behaves differently whether its
operands are integer or float. The difference between SmallInt and
BigInt is not observable (i.e. they are both integers).
The following table summarizes the observable types:
@multitable @columnfractions .3 .3 .3
@headitem Internal type @tab Observable type@* (standard mode) @tab Observable type@* (bigint mode)
@item SmallInt @tab number @tab bigint
@item BigInt @tab bigint @tab bigint
@item Float @tab number @tab number
@end multitable
@section Changes that introduce incompatibilities with Javascript
@subsection Standard mode
There is no incompatibility with Javascript.
@subsection Bigint mode
The following changes are visible:
@itemize
@item Integer and Float are different types. Constants are typed. For example: @code{typeof 1.0 === "number"} and @code{typeof 1 === "bigint"}. Another consequence is that @code{1.0 === 1} is false.
@item The range of integers is unlimited. In standard mode: @code{2**53 + 1 === 2**53}. This is no longer true with the bignum extensions.
@item Binary bitwise operators do not truncate to 32 bits i.e. @code{0x800000000 | 1 === 0x800000001} while it gives @code{1} in standard mode.
@item Bitwise shift operators do not truncate to 32 bits and do not mask the shift count with @code{0x1f} i.e. @code{1 << 32 === 4294967296} while it gives @code{1} in standard mode. However, the @code{>>>} operator (unsigned right shift) which is useless with bignums keeps its standard mode behavior@footnote{The unsigned right right operator could be removed in bigint mode.}.
@item Operators with integer operands never return the minus zero floating point value as result. Hence @code{Object.is(0, -0) === true}. Use @code{-0.0} to create a minus zero floating point value.
@item Division or modulo with a zero integer dividend raises an exception i.e. @code{1/0} throws a @code{RangeError} exception. However, division by the floating point zero returns Infinity or NaN as in standard mode: @code{1/0.0 === Infinity}. The same holds for zero elevated to a negative integer power i.e. @code{0**-1} throws a @code{RangeError} exception.
@item The @code{ToPrimitive} abstract operation is called with the @code{"integer"} preferred type when an integer is required (e.g. for bitwise binary or shift operations).
@item The prototype of integers is no longer @code{Number.prototype}. Instead@* @code{Object.getPrototypeOf(1) === BigInt.prototype}. The prototype of floats remains Number.prototype.
@item If the TC39 BigInt proposal is supported, there is no observable difference between integers and @code{bigint}s.
@end itemize
@section Operators
@subsection Arithmetic operators
The operands are converted to number values as in normal
Javascript. Then the general case is that an Integer is returned if
both operands are Integer. Otherwise, a float is returned.
The @code{+} operator also accepts strings as input and behaves like
standard Javascript in this case.
The binary operator @code{%} returns the truncated remainder of the
division.
The binary operator @code{%} in math mode returns the Euclidian
remainder of the division i.e. it is always positive.
The binary operator @code{/} returns a float.
The binary operator @code{/} in math mode returns a float if one of
the operands is float. Otherwise, @code{BigInt[Symbol.operatorMathDiv]} is
invoked (and returns a fraction for example).
When the result is an Integer type, a dividend of zero yields a
RangeError exception.
The returned type of @code{a ** b} or @code{a ^ b} (math mode) is
Float if @math{a} or @math{b} are Float. If @math{a} and @math{b} are
integers:
@itemize
@item @math{a = 0} and @math{b < 0} generates a RangeError exception
@item @math{|a| \geq 2} and @math{b < 0} returns a Float in bigint mode. In math mode, @code{BigInt[Symbol.operatorMathPow]} is invoked (and returns a fraction for example)
@item otherwise an integer is returned.
@end itemize
The unary @code{-} and unary @code{+} return the same type as their
operand. They performs no floating point rounding when the result is a
float.
The unary operators @code{++} and @code{--} return the same type as
their operand.
In standard mode:
If the operator returns an Integer and that the result fits a
SmallInt, it is converted to SmallInt. Otherwise, the Integer is
converted to a Float.
In bigint mode:
If the operator returns an Integer and that the result fits a
SmallInt, it is converted to SmallInt. Otherwise it is a BigInt.
@subsection Logical operators
In standard mode:
The operands have their standard behavior. If the result fits a
SmallInt it is converted to a SmallInt. Otherwise it is a Float.
In bigint mode:
The operands are converted to integer values. The floating point
values are converted to integer by rounding them to zero.
The logical operators are defined assuming the integers are
represented in two complement notation.
For @code{<<} and @code{<<}, the shift can be positive or negative. So
@code{a << b} is defined as @math{\lfloor a/2^{-b} \rfloor} and
@code{a >> b} is defined as @math{\lfloor a/2^{b} \rfloor}.
The operator @code{>>>} is supported for backward compatibility and
behaves the same way as Javascript i.e. implicit conversion to @code{Uint32}.
If the result fits a SmallInt it is converted to a SmallInt. Otherwise
it is a BigInt.
@subsection Relational operators
The relational operators <, <=, >, >=, ==, != work as expected with
integers and floating point numbers (e.g. @code{1.0 == 1} is true).
The strict equality operators === and !== have the usual Javascript
semantics. In particular, different types never equal, so @code{1.0
=== 1} is false.
@section Number literals
Number literals in bigint mode have a slightly different behavior than
in standard Javascript:
@enumerate
@item
A number literal without a decimal point or an exponent is considered
as an Integer. Otherwise it is a Float.
@item
Hexadecimal, octal or binary floating point literals are accepted with
a decimal point or an exponent. The exponent is specified with the
@code{p} letter assuming a base 2. The same convention is used by
C99. Example: @code{0x1p3} is the same as @code{8.0}.
@end enumerate
@section Builtin Object changes
@subsection @code{BigInt} function
The @code{BigInt} function cannot be invoked as a constructor. When
invoked as a function, it converts its first parameter to an
integer. When a floating point number is given as parameter, it is
truncated to an integer with infinite precision.
@code{BigInt} properties:
@table @code
@item asIntN(bits, a)
Set @math{b=a \pmod{2^{bits}}}. Return @math{b} if @math{b < 2^{bits-1}}
otherwise @math{b-2^{bits}}.
@item asUintN(bits, a)
Return @math{a \pmod{2^{bits}}}.
@item tdiv(a, b)
Return @math{trunc(a/b)}. @code{b = 0} raises a RangeError
exception.
@item fdiv(a, b)
Return @math{\lfloor a/b \rfloor}. @code{b = 0} raises a RangeError
exception.
@item cdiv(a, b)
Return @math{\lceil a/b \rceil}. @code{b = 0} raises a RangeError
exception.
@item ediv(a, b)
Return @math{sgn(b) \lfloor a/{|b|} \rfloor} (Euclidian
division). @code{b = 0} raises a RangeError exception.
@item tdivrem(a, b)
@item fdivrem(a, b)
@item cdivrem(a, b)
@item edivrem(a, b)
Return an array of two elements. The first element is the quotient,
the second is the remainder. The same rounding is done as the
corresponding division operation.
@item sqrt(a)
Return @math{\lfloor \sqrt(a) \rfloor}. A RangeError exception is
raised if @math{a < 0}.
@item sqrtrem(a)
Return an array of two elements. The first element is @math{\lfloor
\sqrt{a} \rfloor}. The second element is @math{a-\lfloor \sqrt{a}
\rfloor^2}. A RangeError exception is raised if @math{a < 0}.
@item floorLog2(a)
Return -1 if @math{a \leq 0} otherwise return @math{\lfloor \log2(a) \rfloor}.
@item ctz(a)
Return the number of trailing zeros in the two's complement binary representation of a. Return -1 if @math{a=0}.
@end table
@subsection @code{BigInt.prototype}
It is a normal object.
@subsection @code{Number} constructor
The number constructor returns its argument rounded to a Float using
the global floating point environement. In bigint mode, the Number
constructor returns a Float. In standard mode, it returns a SmallInt
if the value fits it, otherwise a Float.
@subsection @code{Number.prototype}
The following properties are modified:
@table @code
@item toString(radix)
In bigint mode, integers are converted to the specified radix with
infinite precision.
@item toPrecision(p)
@item toFixed(p)
@item toExponential(p)
In bigint mode, integers are accepted and converted to string with
infinite precision.
@item parseInt(string, radix)
In bigint mode, an integer is returned and the conversion is done with
infinite precision.
@end table
@subsection @code{Math} object
The following properties are modified:
@table @code
@item abs(x)
Absolute value. Return an integer if @code{x} is an Integer. Otherwise
return a Float. No rounding is performed.
@item min(a, b)
@item max(a, b)
No rounding is performed. The returned type is the same one as the
minimum (resp. maximum) value.
@end table
@chapter Arbitrarily large floating point numbers
@section Introduction
This extension adds the @code{BigFloat} primitive type. The
@code{BigFloat} type represents floating point numbers are in base 2
with the IEEE 754 semantics. A floating
point number is represented as a sign, mantissa and exponent. The
special values @code{NaN}, @code{+/-Infinity}, @code{+0} and @code{-0}
are supported. The mantissa and exponent can have any bit length with
an implementation specific minimum and maximum.
@section Floating point rounding
Each floating point operation operates with infinite precision and
then rounds the result according to the specified floating point
environment (@code{BigFloatEnv} object). The status flags of the
environment are also set according to the result of the operation.
If no floating point environment is provided, the global floating
point environment is used.
The rounding mode of the global floating point environment is always
@code{RNDN} (``round to nearest with ties to even'')@footnote{The
rationale is that the rounding mode changes must always be
explicit.}. The status flags of the global environment cannot be
read@footnote{The rationale is to avoid side effects for the built-in
operators.}. The precision of the global environment is
@code{BigFloatEnv.prec}. The number of exponent bits of the global
environment is @code{BigFloatEnv.expBits}. If @code{BigFloatEnv.expBits} is
strictly smaller than the maximum allowed number of exponent bits
(@code{BigFloatEnv.expBitsMax}), then the global environment subnormal
flag is set to @code{true}. Otherwise it is set to @code{false};
For example, @code{prec = 53} and @code{ expBits = 11} give exactly
the same precision as the IEEE 754 64 bit floating point type. It is
the default floating point precision.
The global floating point environment can only be modified temporarily
when calling a function (see @code{BigFloatEnv.setPrec}). Hence a
function can change the global floating point environment for its
callees but not for its caller.
@section Operators
The builtin operators are extended so that a BigFloat is returned if
at least one operand is a BigFloat. The computations are always done
with infinite precision and rounded according to the global floating
point environment.
@code{typeof} applied on a @code{BigFloat} returns @code{bigfloat}.
BigFloat can be compared with all the other numeric types and the
result follows the expected mathematical relations.
However, since BigFloat and Number are different types they are never
equal when using the strict comparison operators (e.g. @code{0.0 ===
0.0l} is false).
@section BigFloat literals
BigFloat literals are floating point numbers with a trailing @code{l}
suffix. BigFloat literals have an infinite precision. They are rounded
according to the global floating point environment when they are
evaluated.@footnote{Base 10 floating point literals cannot usually be
exactly represented as base 2 floating point number. In order to
ensure that the literal is represented accurately with the current
precision, it must be evaluated at runtime.}
@section Builtin Object changes
@subsection @code{BigFloat} function
The @code{BigFloat} function cannot be invoked as a constructor. When
invoked as a function: the parameter is converted to a primitive
type. If the result is a numeric type, it is converted to BigFloat
without rounding. If the result is a string, it is converted to
BigFloat using the precision of the global floating point environment.
@code{BigFloat} properties:
@table @code
@item LN2
@item PI
Getter. Return the value of the corresponding mathematical constant
rounded to nearest, ties to even with the current global
precision. The constant values are cached for small precisions.
@item MIN_VALUE
@item MAX_VALUE
@item EPSILON
Getter. Return the minimum, maximum and epsilon @code{BigFloat} values
(same definition as the corresponding @code{Number} constants).
@item fpRound(a[, e])
Round the floating point number @code{a} according to the floating
point environment @code{e} or the global environment if @code{e} is
undefined.
@item parseFloat(a[, radix[, e]])
Parse the string @code{a} as a floating point number in radix
@code{radix}. The radix is 0 (default) or from 2 to 36. The radix 0
means radix 10 unless there is a hexadecimal or binary prefix. The
result is rounded according to the floating point environment @code{e}
or the global environment if @code{e} is undefined.
@item add(a, b[, e])
@item sub(a, b[, e])
@item mul(a, b[, e])
@item div(a, b[, e])
Perform the specified floating point operation and round the floating
point number @code{a} according to the floating point environment
@code{e} or the global environment if @code{e} is undefined. If
@code{e} is specified, the floating point status flags are updated.
@item floor(x[, e])
@item ceil(x[, e])
@item round(x[, e])
@item trunc(x[, e])
Round to integer. A rounded @code{BigFloat} is returned. @code{e} is an
optional floating point environment.
@item fmod(x, y[, e])
@item remainder(x, y[, e])
Floating point remainder. The quotient is truncated to zero (fmod) or
to the nearest integer with ties to even (remainder). @code{e} is an
optional floating point environment.
@item sqrt(x[, e])
Square root. Return a rounded floating point number. @code{e} is an
optional floating point environment.
@item sin(x[, e])
@item cos(x[, e])
@item tan(x[, e])
@item asin(x[, e])
@item acos(x[, e])
@item atan(x[, e])
@item atan2(x, y[, e])
@item exp(x[, e])
@item log(x[, e])
@item pow(x, y[, e])
Transcendental operations. Return a rounded floating point
number. @code{e} is an optional floating point environment.
@end table
@subsection @code{BigFloat.prototype}
The following properties are modified:
@table @code
@item toString(radix)
For floating point numbers:
@itemize
@item
If the radix is a power of two, the conversion is done with infinite
precision.
@item
Otherwise, the number is rounded to nearest with ties to even using
the global precision. It is then converted to string using the minimum
number of digits so that its conversion back to a floating point using
the global precision and round to nearest gives the same number.
@end itemize
@item toPrecision(p[, rnd_mode])
@item toFixed(p[, rnd_mode])
@item toExponential(p[, rnd_mode])
Same semantics as the corresponding @code{Number} functions with
BigFloats. There is no limit on the accepted precision @code{p}. The
rounding mode can be optionally specified. It is set by default to
@code{BigFloatEnv.RNDNA}.
@end table
@subsection @code{BigFloatEnv} constructor
The @code{BigFloatEnv([p, [,rndMode]]} constructor cannot be invoked as a
function. The floating point environment contains:
@itemize
@item the mantissa precision in bits
@item the exponent size in bits assuming an IEEE 754 representation;
@item the subnormal flag (if true, subnormal floating point numbers can
be generated by the floating point operations).
@item the rounding mode
@item the floating point status. The status flags can only be set by the floating point operations. They can be reset with @code{BigFloatEnv.prototype.clearStatus()} or with the various status flag setters.
@end itemize
@code{new BigFloatEnv([p, [,rndMode]]} creates a new floating point
environment. The status flags are reset. If no parameter is given the
precision, exponent bits and subnormal flags are copied from the
global floating point environment. Otherwise, the precision is set to
@code{p}, the number of exponent bits is set to @code{expBitsMax} and the
subnormal flags is set to @code{false}. If @code{rndMode} is
@code{undefined}, the rounding mode is set to @code{RNDN}.
@code{BigFloatEnv} properties:
@table @code
@item prec
Getter. Return the precision in bits of the global floating point
environment. The initial value is @code{53}.
@item expBits
Getter. Return the exponent size in bits of the global floating point
environment assuming an IEEE 754 representation. If @code{expBits <
expBitsMax}, then subnormal numbers are supported. The initial value
is @code{11}.
@item setPrec(f, p[, e])
Set the precision of the global floating point environment to @code{p}
and the exponent size to @code{e} then call the function
@code{f}. Then the Float precision and exponent size are reset to
their precious value and the return value of @code{f} is returned (or
an exception is raised if @code{f} raised an exception). If @code{e}
is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}. @code{p}
must be >= 53 and @code{e} must be >= 11 so that the global precision
is at least equivalent to the IEEE 754 64 bit doubles.
@item precMin
Read-only integer. Return the minimum allowed precision. Must be at least 2.
@item precMax
Read-only integer. Return the maximum allowed precision. Must be at least 53.
@item expBitsMin
Read-only integer. Return the minimum allowed exponent size in
bits. Must be at least 3.
@item expBitsMax
Read-only integer. Return the maximum allowed exponent size in
bits. Must be at least 11.
@item RNDN
Read-only integer. Round to nearest, with ties to even rounding mode.
@item RNDZ
Read-only integer. Round to zero rounding mode.
@item RNDD
Read-only integer. Round to -Infinity rounding mode.
@item RNDU
Read-only integer. Round to +Infinity rounding mode.
@item RNDNA
Read-only integer. Round to nearest, with ties away from zero rounding mode.
@item RNDNU
Read-only integer. Round to nearest, with ties to +Infinity rounding mode.
@item RNDF@footnote{Could be removed in case a deterministic behvior for floating point operations is required.}
Read-only integer. Faithful rounding mode. The result is
non-deterministicly rounded to -Infinity or +Infinity. This rounding
mode usually gives a faster and deterministic running time for the
floating point operations.
@end table
@code{BigFloatEnv.prototype} properties:
@table @code
@item prec
Getter and setter (Integer). Return or set the precision in bits.
@item expBits
Getter and setter (Integer). Return or set the exponent size in bits
assuming an IEEE 754 representation.
@item rndMode
Getter and setter (Integer). Return or set the rounding mode.
@item subnormal
Getter and setter (Boolean). subnormal flag. It is false when
@code{expBits = expBitsMax}.
@item clearStatus()
Clear the status flags.
@item invalidOperation
@item divideByZero
@item overflow
@item underflow
@item inexact
Getter and setter (Boolean). Status flags.
@end table
@subsection @code{Math} object
The following properties are modified:
@table @code
@item abs(x)
Absolute value. If @code{x} is a BigFloat, its absolute value is
returned as a BigFloat. No rounding is performed.
@item min(a, b)
@item max(a, b)
The returned type is the same one as the minimum (resp. maximum)
value, so @code{BigFloat} values are accepted. When a @code{BigFloat}
is returned, no rounding is performed.
@end table
@chapter Math mode
@section Introduction
A new @emph{math mode} is enabled with the @code{"use math"}
directive. @code{"use bigint"} is implied in math mode. With this
mode, writing mathematical expressions is more intuitive, exact
results (fractions) can be computed for all operators and floating
point literals have the @code{BigFloat} type by default.
It propagates the same way as the @emph{strict mode}. In
this mode:
@itemize
@item The @code{^} operator is a similar to the power operator (@code{**}), except that @code{a ^ b} invokes @code{BigInt[Symbol.operatorMathPow]} if @math{a} and @math{b} are integers and @math{|a| \geq 2} and @math{b < 0}.
@item The power operator (both @code{^} and @code{**}) grammar is modified so that @code{-2^2} is allowed and yields @code{-4}.
@item The logical xor operator is still available with the @code{^^} operator.
@item The division operator invokes @code{BigInt[Symbol.operatorMathDiv]} in case both operands are integers.
@item The modulo operator returns the Euclidian remainder (always positive) instead of the truncated remainder.
@item Floating point literals are @code{BigFloat} by default (i.e. a @code{l} suffix is implied).
@end itemize
@section Builtin Object changes
@subsection @code{Symbol} constructor
The following global symbols are added for the operator overloading:
@table @code
@item operatorMathDiv
@item operatorMathMod
@item operatorMathPow
@end table
@section Remaining issues
@enumerate
@item New functions (e.g. @code{Math.div} and @code{Math.mod}) could be added to be able to call the normal division and modulo operators when in math mode.
@item A new floating point literal suffix could be added for @code{Number} literals.
@end enumerate
@bye

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,817 @@
\input texinfo
@iftex
@afourpaper
@headings double
@end iftex
@titlepage
@afourpaper
@sp 7
@center @titlefont{QuickJS Javascript Engine}
@sp 3
@end titlepage
@setfilename spec.info
@settitle QuickJS Javascript Engine
@contents
@chapter Introduction
QuickJS is a small and embeddable Javascript engine. It supports the
ES2019 specification including modules, asynchronous
generators and proxies.
It optionally supports mathematical extensions such as big integers
(BigInt), big floating point numbers (BigFloat) and operator
overloading.
@section Main Features
@itemize
@item Small and easily embeddable: just a few C files, no external dependency, 180 KiB of x86 code for a simple ``hello world'' program.
@item Fast interpreter with very low startup time: runs the 56000 tests of the ECMAScript Test Suite@footnote{@url{https://github.com/tc39/test262}} in about 100 seconds on a single core of a desktop PC. The complete life cycle of a runtime instance completes in less than 300 microseconds.
@item Almost complete ES2019 support including modules, asynchronous
generators and full Annex B support (legacy web compatibility).
@item Passes 100% of the ECMAScript Test Suite tests.
@item Can compile Javascript sources to executables with no external dependency.
@item Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal.
@item Mathematical extensions: BigInt, BigFloat, operator overloading, bigint mode, math mode.
@item Command line interpreter with contextual colorization and completion implemented in Javascript.
@item Small built-in standard library with C library wrappers.
@end itemize
@chapter Usage
@section Installation
A Makefile is provided to compile the engine on Linux or MacOS/X. A
preliminary Windows support is available thru cross compilation on a
Linux host with the MingGW tools.
Edit the top of the @code{Makefile} if you wish to select specific
options then run @code{make}.
You can type @code{make install} as root if you wish to install the binaries and support files to
@code{/usr/local} (this is not necessary to use QuickJS).
@section Quick start
@code{qjs} is the command line interpreter (Read-Eval-Print Loop). You can pass
Javascript files and/or expressions as arguments to execute them:
@example
./qjs examples/hello.js
@end example
@code{qjsc} is the command line compiler:
@example
./qjsc -o hello examples/hello.js
./hello
@end example
generates a @code{hello} executable with no external dependency.
@code{qjsbn} and @code{qjscbn} are the corresponding interpreter and
compiler with the mathematical extensions:
@example
./qjsbn examples/pi.js 1000
@end example
displays 1000 digits of PI.
@example
./qjsbnc -o pi examples/pi.js
./pi 1000
@end example
compiles and executes the PI program.
@section Command line options
@subsection @code{qjs} interpreter
@verbatim
usage: qjs [options] [files]
@end verbatim
Options are:
@table @code
@item -h
@item --help
List options.
@item -e @code{EXPR}
@item --eval @code{EXPR}
Evaluate EXPR.
@item -i
@item --interactive
Go to interactive mode (it is not the default when files are provided on the command line).
@item -m
@item --module
Load as ES6 module (default if .mjs file extension).
@end table
Advanced options are:
@table @code
@item -d
@item --dump
Dump the memory usage stats.
@item -q
@item --quit
just instantiate the interpreter and quit.
@end table
@subsection @code{qjsc} compiler
@verbatim
usage: qjsc [options] [files]
@end verbatim
Options are:
@table @code
@item -c
Only output bytecode in a C file. The default is to output an executable file.
@item -e
Output @code{main()} and bytecode in a C file. The default is to output an
executable file.
@item -o output
Set the output filename (default = @file{out.c} or @file{a.out}).
@item -N cname
Set the C name of the generated data.
@item -m
Compile as Javascript module (default if @file{.mjs} extension).
@item -M module_name[,cname]
Add initialization code for an external C module. See the
@code{c_module} example.
@item -x
Byte swapped output (only used for cross compilation).
@item -flto
Use link time optimization. The compilation is slower but the
executable is smaller and faster. This option is automatically set
when the @code{-fno-x} options are used.
@item -fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise]
Disable selected language features to produce a smaller executable file.
@end table
@section @code{qjscalc} application
The @code{qjscalc} application is a superset of the @code{qjsbn}
command line interpreter implementing a Javascript calculator with
arbitrarily large integer and floating point numbers, fractions,
complex numbers, polynomials and matrices. The source code is in
@file{qjscalc.js}. More documentation and a web version are available at
@url{http://numcalc.com}.
@section Built-in tests
Run @code{make test} to run the few built-in tests included in the
QuickJS archive.
@section Test262 (ECMAScript Test Suite)
A test262 runner is included in the QuickJS archive.
For reference, the full test262 tests are provided in the archive
@file{qjs-tests-yyyy-mm-dd.tar.xz}. You just need to untar it into the
QuickJS source code directory.
Alternatively, the test262 tests can be installed with:
@example
git clone https://github.com/tc39/test262.git test262
cd test262
git checkout 94b1e80ab3440413df916cd56d29c5a2fa2ac451
patch -p1 < ../tests/test262.patch
cd ..
@end example
The patch adds the implementation specific @code{harness} functions
and optimizes the inefficient RegExp character classes and Unicode
property escapes tests (the tests themselves are not modified, only a
slow string initialization function is optimized).
The tests can be run with
@example
make test2
@end example
For more information, run @code{./run-test262} to see the options of
the test262 runner. The configuration files @code{test262.conf} and
@code{test262bn.conf} contain the options to run the various tests.
@chapter Specifications
@section Language support
@subsection ES2019 support
The ES2019 specification
@footnote{@url{https://tc39.github.io/ecma262/}} is almost fully
supported including the Annex B (legacy web compatibility) and the
Unicode related features. The following features are not supported
yet:
@itemize
@item Realms (althougth the C API supports different runtimes and contexts)
@item Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.}
@end itemize
@subsection JSON
The JSON parser is currently more tolerant than the specification.
@subsection ECMA402
ECMA402 (Internationalization API) is not supported.
@subsection Extensions
@itemize
@item The directive @code{"use strip"} indicates that the debug information (including the source code of the functions) should not be retained to save memory. As @code{"use strict"}, the directive can be global to a script or local to a function.
@item The first line of a script beginning with @code{#!} is ignored.
@end itemize
@subsection Mathematical extensions
The mathematical extensions are available in the @code{qjsbn} version and are fully
backward compatible with standard Javascript. See @code{jsbignum.pdf}
for more information.
@itemize
@item The @code{BigInt} (big integers) TC39 proposal is supported.
@item @code{BigFloat} support: arbitrary large floating point numbers in base 2.
@item Operator overloading.
@item The directive @code{"use bigint"} enables the bigint mode where integers are @code{BigInt} by default.
@item The directive @code{"use math"} enables the math mode where the division and power operators on integers produce fractions. Floating point literals are @code{BigFloat} by default and integers are @code{BigInt} by default.
@end itemize
@section Modules
ES6 modules are fully supported. The default name resolution is the
following:
@itemize
@item Module names with a leading @code{.} or @code{..} are relative
to the current module path.
@item Module names without a leading @code{.} or @code{..} are system
modules, such as @code{std} or @code{os}.
@item Module names ending with @code{.so} are native modules using the
QuickJS C API.
@end itemize
@section Standard library
The standard library is included by default in the command line
interpreter. It contains the two modules @code{std} and @code{os} and
a few global objects.
@subsection Global objects
@table @code
@item scriptArgs
Provides the command line arguments. The first argument is the script name.
@item print(...args)
Print the arguments separated by spaces and a trailing newline.
@item console.log(...args)
Same as print().
@end table
@subsection @code{std} module
The @code{std} module provides wrappers to the libc @file{stdlib.h}
and @file{stdio.h} and a few other utilities.
Available exports:
@table @code
@item exit(n)
Exit the process.
@item evalScript(str)
Evaluate the string @code{str} as a script (global eval).
@item loadScript(filename)
Evaluate the file @code{filename} as a script (global eval).
@item Error(errno)
@code{std.Error} constructor. Error instances contain the field
@code{errno} (error code) and @code{message} (result of
@code{std.Error.strerror(errno)}).
The constructor contains the following fields:
@table @code
@item EINVAL
@item EIO
@item EACCES
@item EEXIST
@item ENOSPC
@item ENOSYS
@item EBUSY
@item ENOENT
@item EPERM
@item EPIPE
Integer value of common errors (additional error codes may be defined).
@item strerror(errno)
Return a string that describes the error @code{errno}.
@end table
@item open(filename, flags)
Open a file (wrapper to the libc @code{fopen()}). Throws
@code{std.Error} in case of I/O error.
@item tmpfile()
Open a temporary file. Throws @code{std.Error} in case of I/O error.
@item puts(str)
Equivalent to @code{std.out.puts(str)}.
@item printf(fmt, ...args)
Equivalent to @code{std.out.printf(fmt, ...args)}
@item sprintf(fmt, ...args)
Equivalent to the libc sprintf().
@item in
@item out
@item err
Wrappers to the libc file @code{stdin}, @code{stdout}, @code{stderr}.
@item SEEK_SET
@item SEEK_CUR
@item SEEK_END
Constants for seek().
@item global
Reference to the global object.
@item gc()
Manually invoke the cycle removal algorithm. The cycle removal
algorithm is automatically started when needed, so this function is
useful in case of specific memory constraints or for testing.
@item getenv(name)
Return the value of the environment variable @code{name} or
@code{undefined} if it is not defined.
@end table
FILE prototype:
@table @code
@item close()
Close the file.
@item puts(str)
Outputs the string with the UTF-8 encoding.
@item printf(fmt, ...args)
Formatted printf, same formats as the libc printf.
@item flush()
Flush the buffered file.
@item seek(offset, whence)
Seek to a give file position (whence is @code{std.SEEK_*}). Throws a
@code{std.Error} in case of I/O error.
@item tell()
Return the current file position.
@item eof()
Return true if end of file.
@item fileno()
Return the associated OS handle.
@item read(buffer, position, length)
Read @code{length} bytes from the file to the ArrayBuffer @code{buffer} at byte
position @code{position} (wrapper to the libc @code{fread}).
@item write(buffer, position, length)
Write @code{length} bytes to the file from the ArrayBuffer @code{buffer} at byte
position @code{position} (wrapper to the libc @code{fread}).
@item getline()
Return the next line from the file, assuming UTF-8 encoding, excluding
the trailing line feed.
@item getByte()
Return the next byte from the file.
@item putByte(c)
Write one byte to the file.
@end table
@subsection @code{os} module
The @code{os} module provides Operating System specific functions:
@itemize
@item low level file access
@item signals
@item timers
@item asynchronous I/O
@end itemize
The OS functions usually return 0 if OK or an OS specific negative
error code.
Available exports:
@table @code
@item open(filename, flags, mode = 0o666)
Open a file. Return a handle or < 0 if error.
@item O_RDONLY
@item O_WRONLY
@item O_RDWR
@item O_APPEND
@item O_CREAT
@item O_EXCL
@item O_TRUNC
POSIX open flags.
@item O_TEXT
(Windows specific). Open the file in text mode. The default is binary mode.
@item close(fd)
Close the file handle @code{fd}.
@item seek(fd, offset, whence)
Seek in the file. Use @code{std.SEEK_*} for @code{whence}.
@item read(fd, buffer, offset, length)
Read @code{length} bytes from the file handle @code{fd} to the
ArrayBuffer @code{buffer} at byte position @code{offset}.
Return the number of read bytes or < 0 if error.
@item write(fd, buffer, offset, length)
Write @code{length} bytes to the file handle @code{fd} from the
ArrayBuffer @code{buffer} at byte position @code{offset}.
Return the number of written bytes or < 0 if error.
@item isatty(fd)
Return @code{true} is @code{fd} is a TTY (terminal) handle.
@item ttyGetWinSize(fd)
Return the TTY size as @code{[width, height]} or @code{null} if not available.
@item ttySetRaw(fd)
Set the TTY in raw mode.
@item remove(filename)
Remove a file. Return 0 if OK or < 0 if error.
@item rename(oldname, newname)
Rename a file. Return 0 if OK or < 0 if error.
@item setReadHandler(fd, func)
Add a read handler to the file handle @code{fd}. @code{func} is called
each time there is data pending for @code{fd}. A single read handler
per file handle is supported. Use @code{func = null} to remove the
hander.
@item setWriteHandler(fd, func)
Add a write handler to the file handle @code{fd}. @code{func} is
called each time data can be written to @code{fd}. A single write
handler per file handle is supported. Use @code{func = null} to remove
the hander.
@item signal(signal, func)
Call the function @code{func} when the signal @code{signal}
happens. Only a single handler per signal number is supported. Use
@code{null} to set the default handler or @code{undefined} to ignore
the signal.
@item SIGINT
@item SIGABRT
@item SIGFPE
@item SIGILL
@item SIGSEGV
@item SIGTERM
POSIX signal numbers.
@item setTimeout(delay, func)
Call the function @code{func} after @code{delay} ms. Return a handle
to the timer.
@item clearTimer(handle)
Cancel a timer.
@item platform
Return a string representing the platform: @code{"linux"}, @code{"darwin"},
@code{"win32"} or @code{"js"}.
@end table
@section QuickJS C API
The C API was designed to be simple and efficient. The C API is
defined in the header @code{quickjs.h}.
@subsection Runtime and contexts
@code{JSRuntime} represents a Javascript runtime corresponding to an
object heap. Several runtimes can exist at the same time but they
cannot exchange objects. Inside a given runtime, no multi-threading is
supported.
@code{JSContext} represents a Javascript context (or Realm). Each
JSContext has its own global objects and system objects. There can be
several JSContexts per JSRuntime and they can share objects, similary
to frames of the same origin sharing Javascript objects in a
web browser.
@subsection JSValue
@code{JSValue} represents a Javascript value which can be a primitive
type or an object. Reference counting is used, so it is important to
explicitely duplicate (@code{JS_DupValue()}, increment the reference
count) or free (@code{JS_FreeValue()}, decrement the reference count)
JSValues.
@subsection C functions
C functions can be created with
@code{JS_NewCFunction()}. @code{JS_SetPropertyFunctionList()} is a
shortcut to easily add functions, setters and getters properties to a
given object.
Unlike other embedded Javascript engines, there is no implicit stack,
so C functions get their parameters as normal C parameters. As a
general rule, C functions take constant @code{JSValue}s as parameters
(so they don't need to free them) and return a newly allocated (=live)
@code{JSValue}.
@subsection Exceptions
Exceptions: most C functions can return a Javascript exception. It
must be explicitely tested and handled by the C code. The specific
@code{JSValue} @code{JS_EXCEPTION} indicates that an exception
occured. The actual exception object is stored in the
@code{JSContext} and can be retrieved with @code{JS_GetException()}.
@subsection Script evaluation
Use @code{JS_Eval()} to evaluate a script or module source.
If the script or module was compiled to bytecode with @code{qjsc},
@code{JS_EvalBinary()} achieves the same result. The advantage is that
no compilation is needed so it is faster and smaller because the compiler
can be removed from the executable if no @code{eval} is required.
Note: the bytecode format is linked to a given QuickJS
version. Moreover, no security check is done before its
execution. Hence the bytecode should not be loaded from untrusted
sources. That's why there is no option to output the bytecode to a
binary file in @code{qjsc}.
@subsection JS Classes
C opaque data can be attached to a Javascript object. The type of the
C opaque data is determined with the class ID (@code{JSClassID}) of
the object. Hence the first step is to register a new class ID and JS
class (@code{JS_NewClassID()}, @code{JS_NewClass()}). Then you can
create objects of this class with @code{JS_NewObjectClass()} and get or
set the C opaque point with
@code{JS_GetOpaque()}/@code{JS_SetOpaque()}.
When defining a new JS class, it is possible to declare a finalizer
which is called when the object is destroyed. A @code{gc_mark} method
can be provided so that the cycle removal algorithm can find the other
objects referenced by this object. Other methods are available to
define exotic object behaviors.
The Class ID are globally allocated (i.e. for all runtimes). The
JSClass are allocated per @code{JSRuntime}. @code{JS_SetClassProto()}
is used to define a prototype for a given class in a given
JSContext. @code{JS_NewObjectClass()} sets this prototype in the
created object.
Examples are available in @file{js_libc.c}.
@subsection C Modules
Native ES6 modules are supported and can be dynamically or statically
linked. Look at the @file{test_bjson} and @file{bjson.so}
examples. The standard library @file{js_libc.c} is also a good example
of a native module.
@subsection Memory handling
Use @code{JS_SetMemoryLimit()} to set a global memory allocation limit
to a given JSRuntime.
Custom memory allocation functions can be provided with
@code{JS_NewRuntime2()}.
The maximum system stack size can be set with @code{JS_SetMaxStackSize()}.
@subsection Execution timeout and interrupts
Use @code{JS_SetInterruptHandler()} to set a callback which is
regularly called by the engine when it is executing code. This
callback can be used to implement an execution timeout.
It is used by the command line interpreter to implement a
@code{Ctrl-C} handler.
@chapter Internals
@section Bytecode
The compiler generates bytecode directly with no intermediate
representation such as a parse tree, hence it is very fast. Several
optimizations passes are done over the generated bytecode.
A stack-based bytecode was chosen because it is simple and generates
compact code.
For each function, the maximum stack size is computed at compile time so that
no runtime stack overflow tests are needed.
A separate compressed line number table is maintained for the debug
information.
Access to closure variables is optimized and is almost as fast as local
variables.
Direct @code{eval} in strict mode is optimized.
@section Executable generation
@subsection @code{qjsc} compiler
The @code{qjsc} compiler generates C sources from Javascript files. By
default the C sources are compiled with the system compiler
(@code{gcc} or @code{clang}).
The generated C source contains the bytecode of the compiled functions
or modules. If a full complete executable is needed, it also
contains a @code{main()} function with the necessary C code to initialize the
Javascript engine and to load and execute the compiled functions and
modules.
Javascript code can be mixed with C modules.
In order to have smaller executables, specific Javascript features can
be disabled, in particular @code{eval} or the regular expressions. The
code removal relies on the Link Time Optimization of the system
compiler.
@subsection Binary JSON
@code{qjsc} works by compiling scripts or modules and then serializing
them to a binary format. A subset of this format (without functions or
modules) can be used as binary JSON. The example @file{test_bjson.js}
shows how to use it.
Warning: the binary JSON format may change without notice, so it
should not be used to store persistent data. The @file{test_bjson.js}
example is only used to test the binary object format functions.
@section Runtime
@subsection Strings
Strings are stored either as an 8 bit or a 16 bit array of
characters. Hence random access to characters is always fast.
The C API provides functions to convert Javascript Strings to C UTF-8 encoded
strings. The most common case where the Javascript string contains
only ASCII characters involves no copying.
@subsection Objects
The object shapes (object prototype, property names and flags) are shared
between objects to save memory.
Arrays with no holes (except at the end of the array) are optimized.
TypedArray accesses are optimized.
@subsection Atoms
Object property names and some strings are stored as Atoms (unique
strings) to save memory and allow fast comparison. Atoms are
represented as a 32 bit integer. Half of the atom range is reserved for
immediate integer literals from @math{0} to @math{2^{31}-1}.
@subsection Numbers
Numbers are represented either as 32-bit signed integers or 64-bit IEEE-754
floating point values. Most operations have fast paths for the 32-bit
integer case.
@subsection Garbage collection
Reference counting is used to free objects automatically and
deterministically. A separate cycle removal pass is done when the allocated
memory becomes too large. The cycle removal algorithm only uses the
reference counts and the object content, so no explicit garbage
collection roots need to be manipulated in the C code.
@subsection JSValue
It is a Javascript value which can be a primitive type (such as
Number, String, ...) or an Object. NaN boxing is used in the 32-bit version
to store 64-bit floating point numbers. The representation is
optimized so that 32-bit integers and reference counted values can be
efficiently tested.
In 64-bit code, JSValue are 128-bit large and no NaN boxing is used. The
rationale is that in 64-bit code memory usage is less critical.
In both cases (32 or 64 bits), JSValue exactly fits two CPU registers,
so it can be efficiently returned by C functions.
@subsection Function call
The engine is optimized so that function calls are fast. The system
stack holds the Javascript parameters and local variables.
@section RegExp
A specific regular expression engine was developped. It is both small
and efficient and supports all the ES2019 features including the
Unicode properties. As the Javascript compiler, it directly generates
bytecode without a parse tree.
Backtracking with an explicit stack is used so that there is no
recursion on the system stack. Simple quantizers are specifically
optimized to avoid recursions.
Infinite recursions coming from quantizers with empty terms are
avoided.
The full regexp library weights about 15 KiB (x86 code), excluding the
Unicode library.
@section Unicode
A specific Unicode library was developped so that there is no
dependency on an external large Unicode library such as ICU. All the
Unicode tables are compressed while keeping a reasonnable access
speed.
The library supports case conversion, Unicode normalization, Unicode
script queries, Unicode general category queries and all Unicode
binary properties.
The full Unicode library weights about 45 KiB (x86 code).
@section BigInt and BigFloat
BigInt and BigFloat are implemented with the @code{libbf}
library@footnote{@url{https://bellard.org/libbf}}. It weights about 60
KiB (x86 code) and provides arbitrary precision IEEE 754 floating
point operations and transcendental functions with exact rounding.
@chapter License
QuickJS is released under the MIT license.
Unless otherwise specified, the QuickJS sources are copyright Fabrice
Bellard and Charlie Gordon.
@bye

View File

@ -0,0 +1,6 @@
/* example of JS module importing a C module */
import { fib } from "./fib.so";
console.log("Hello World");
console.log("fib(10)=", fib(10));

View File

@ -0,0 +1,72 @@
/*
* QuickJS: Example of C module
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#include "../quickjs.h"
#define countof(x) (sizeof(x) / sizeof((x)[0]))
static int fib(int n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
static JSValue js_fib(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int n, res;
if (JS_ToInt32(ctx, &n, argv[0]))
return JS_EXCEPTION;
res = fib(n);
return JS_NewInt32(ctx, res);
}
static const JSCFunctionListEntry js_fib_funcs[] = {
JS_CFUNC_DEF("fib", 1, js_fib ),
};
static int js_fib_init(JSContext *ctx, JSModuleDef *m)
{
return JS_SetModuleExportList(ctx, m, js_fib_funcs,
countof(js_fib_funcs));
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_fib
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_fib_init);
if (!m)
return NULL;
JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs));
return m;
}

View File

@ -0,0 +1,10 @@
/* fib module */
export function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}

View File

@ -0,0 +1 @@
console.log("Hello World");

View File

@ -0,0 +1,6 @@
/* example of JS module */
import { fib } from "./fib_module.js";
console.log("Hello World");
console.log("fib(10)=", fib(10));

View File

@ -0,0 +1,66 @@
/*
* PI computation in Javascript using the QuickJS bignum extensions
*/
"use strict";
"use bigint";
/* compute PI with a precision of 'prec' bits */
function calc_pi(prec) {
const CHUD_A = 13591409;
const CHUD_B = 545140134;
const CHUD_C = 640320;
const CHUD_C3 = 10939058860032000; /* C^3/24 */
const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
/* return [P, Q, G] */
function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
if (a == (b - 1)) {
G = (2 * b - 1) * (6 * b - 1) * (6 * b - 5);
P = BigFloat(G * (CHUD_B * b + CHUD_A));
if (b & 1)
P = -P;
G = BigFloat(G);
Q = BigFloat(b * b * b * CHUD_C3);
} else {
c = (a + b) >> 1;
[P1, Q1, G1] = chud_bs(a, c, true);
[P2, Q2, G2] = chud_bs(c, b, need_G);
P = P1 * Q2 + P2 * G1;
Q = Q1 * Q2;
if (need_G)
G = G1 * G2;
else
G = 0;
}
return [P, Q, G];
}
var n, P, Q, G;
/* number of serie terms */
n = Math.ceil(BigFloatEnv.prec / CHUD_BITS_PER_TERM) + 10;
[P, Q, G] = chud_bs(0, n, false);
Q = Q / (P + Q * CHUD_A);
G = (CHUD_C / 12) * BigFloat.sqrt(CHUD_C);
return Q * G;
}
(function() {
var r, n_digits, n_bits;
if (typeof scriptArgs != "undefined") {
if (scriptArgs.length < 2) {
print("usage: pi n_digits");
return;
}
n_digits = scriptArgs[1];
} else {
n_digits = 1000;
}
n_bits = Math.ceil(n_digits * Math.log2(10));
/* we add more bits to reduce the probability of bad rounding for
the last digits */
BigFloatEnv.setPrec( () => {
r = calc_pi();
print(r.toFixed(n_digits, BigFloatEnv.RNDZ));
}, n_bits + 32);
})();

View File

@ -0,0 +1,918 @@
/*
* Javascript Compressor
*
* Copyright (c) 2008-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include "cutils.h"
typedef struct JSToken {
int tok;
char buf[20];
char *str;
int len;
int size;
int line_num; /* line number for start of token */
int lines; /* number of embedded linefeeds in token */
} JSToken;
enum {
TOK_EOF = 256,
TOK_IDENT,
TOK_STR1,
TOK_STR2,
TOK_STR3,
TOK_NUM,
TOK_COM,
TOK_LCOM,
};
void tok_reset(JSToken *tt)
{
if (tt->str != tt->buf) {
free(tt->str);
tt->str = tt->buf;
tt->size = sizeof(tt->buf);
}
tt->len = 0;
}
void tok_add_ch(JSToken *tt, int c)
{
if (tt->len + 1 > tt->size) {
tt->size *= 2;
if (tt->str == tt->buf) {
tt->str = malloc(tt->size);
memcpy(tt->str, tt->buf, tt->len);
} else {
tt->str = realloc(tt->str, tt->size);
}
}
tt->str[tt->len++] = c;
}
FILE *infile;
const char *filename;
int output_line_num;
int line_num;
int ch;
JSToken tokc;
int skip_mask;
#define DEFINE_MAX 20
char *define_tab[DEFINE_MAX];
int define_len;
void error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (filename) {
fprintf(stderr, "%s:%d: ", filename, line_num);
} else {
fprintf(stderr, "jscompress: ");
}
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
void define_symbol(const char *def)
{
int i;
for (i = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i]))
return;
}
if (define_len >= DEFINE_MAX)
error("too many defines");
define_tab[define_len++] = strdup(def);
}
void undefine_symbol(const char *def)
{
int i, j;
for (i = j = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i])) {
free(define_tab[i]);
} else {
define_tab[j++] = define_tab[i];
}
}
define_len = j;
}
const char *find_symbol(const char *def)
{
int i;
for (i = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i]))
return "1";
}
return NULL;
}
void next(void);
void nextch(void)
{
ch = fgetc(infile);
if (ch == '\n')
line_num++;
}
int skip_blanks(void)
{
for (;;) {
next();
if (tokc.tok != ' ' && tokc.tok != '\t' &&
tokc.tok != TOK_COM && tokc.tok != TOK_LCOM)
return tokc.tok;
}
}
void parse_directive(void)
{
int ifdef, mask = skip_mask;
/* simplistic preprocessor:
#define / #undef / #ifdef / #ifndef / #else / #endif
no symbol substitution.
*/
skip_mask = 0; /* disable skipping to parse preprocessor line */
nextch();
if (skip_blanks() != TOK_IDENT)
error("expected preprocessing directive after #");
if (!strcmp(tokc.str, "define")) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #define");
define_symbol(tokc.str);
} else if (!strcmp(tokc.str, "undef")) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #undef");
undefine_symbol(tokc.str);
} else if ((ifdef = 1, !strcmp(tokc.str, "ifdef")) ||
(ifdef = 0, !strcmp(tokc.str, "ifndef"))) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #ifdef/#ifndef");
mask = (mask << 2) | 2 | ifdef;
if (find_symbol(tokc.str))
mask ^= 1;
} else if (!strcmp(tokc.str, "else")) {
if (!(mask & 2))
error("#else without a #if");
mask ^= 1;
} else if (!strcmp(tokc.str, "endif")) {
if (!(mask & 2))
error("#endif without a #if");
mask >>= 2;
} else {
error("unsupported preprocessing directive");
}
if (skip_blanks() != '\n')
error("extra characters on preprocessing line");
skip_mask = mask;
}
/* return -1 if invalid char */
static int hex_to_num(int ch)
{
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else if (ch >= '0' && ch <= '9')
return ch - '0';
else
return -1;
}
void next(void)
{
again:
tok_reset(&tokc);
tokc.line_num = line_num;
tokc.lines = 0;
switch(ch) {
case EOF:
tokc.tok = TOK_EOF;
if (skip_mask)
error("missing #endif");
break;
case 'a' ... 'z':
case 'A' ... 'Z':
case '_':
case '$':
tok_add_ch(&tokc, ch);
nextch();
while ((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
(ch == '_' || ch == '$')) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_IDENT;
break;
case '.':
nextch();
if (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, '.');
goto has_dot;
}
tokc.tok = '.';
break;
case '0':
tok_add_ch(&tokc, ch);
nextch();
if (ch == 'x' || ch == 'X') {
/* hexa */
tok_add_ch(&tokc, ch);
nextch();
while ((ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F') ||
(ch >= '0' && ch <= '9')) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_NUM;
break;
}
goto has_digit;
case '1' ... '9':
tok_add_ch(&tokc, ch);
nextch();
has_digit:
/* decimal */
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
if (ch == '.') {
tok_add_ch(&tokc, ch);
nextch();
has_dot:
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
}
if (ch == 'e' || ch == 'E') {
tok_add_ch(&tokc, ch);
nextch();
if (ch == '+' || ch == '-') {
tok_add_ch(&tokc, ch);
nextch();
}
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_NUM;
break;
case '`':
{
nextch();
while (ch != '`' && ch != EOF) {
if (ch == '\\') {
tok_add_ch(&tokc, ch);
nextch();
if (ch == EOF) {
error("unexpected char after '\\'");
}
tok_add_ch(&tokc, ch);
} else {
tok_add_ch(&tokc, ch);
nextch();
}
}
nextch();
tok_add_ch(&tokc, 0);
tokc.tok = TOK_STR3;
}
break;
case '\"':
case '\'':
{
int n, i, c, hex_digit_count;
int quote_ch;
quote_ch = ch;
nextch();
while (ch != quote_ch && ch != EOF) {
if (ch == '\\') {
nextch();
switch(ch) {
case 'n':
tok_add_ch(&tokc, '\n');
nextch();
break;
case 'r':
tok_add_ch(&tokc, '\r');
nextch();
break;
case 't':
tok_add_ch(&tokc, '\t');
nextch();
break;
case 'v':
tok_add_ch(&tokc, '\v');
nextch();
break;
case '\"':
case '\'':
case '\\':
tok_add_ch(&tokc, ch);
nextch();
break;
case '0' ... '7':
n = 0;
while (ch >= '0' && ch <= '7') {
n = n * 8 + (ch - '0');
nextch();
}
tok_add_ch(&tokc, n);
break;
case 'x':
case 'u':
if (ch == 'x')
hex_digit_count = 2;
else
hex_digit_count = 4;
nextch();
n = 0;
for(i = 0; i < hex_digit_count; i++) {
c = hex_to_num(ch);
if (c < 0)
error("unexpected char after '\\x'");
n = n * 16 + c;
nextch();
}
if (n >= 256)
error("unicode is currently unsupported");
tok_add_ch(&tokc, n);
break;
default:
error("unexpected char after '\\'");
}
} else {
/* XXX: should refuse embedded newlines */
tok_add_ch(&tokc, ch);
nextch();
}
}
nextch();
tok_add_ch(&tokc, 0);
if (quote_ch == '\'')
tokc.tok = TOK_STR1;
else
tokc.tok = TOK_STR2;
}
break;
case '/':
nextch();
if (ch == '/') {
tok_add_ch(&tokc, '/');
tok_add_ch(&tokc, ch);
nextch();
while (ch != '\n' && ch != EOF) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_LCOM;
} else if (ch == '*') {
int last;
tok_add_ch(&tokc, '/');
tok_add_ch(&tokc, ch);
last = 0;
for(;;) {
nextch();
if (ch == EOF)
error("unterminated comment");
if (ch == '\n')
tokc.lines++;
tok_add_ch(&tokc, ch);
if (last == '*' && ch == '/')
break;
last = ch;
}
nextch();
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_COM;
} else {
tokc.tok = '/';
}
break;
case '#':
parse_directive();
goto again;
case '\n':
/* adjust line number */
tokc.line_num--;
tokc.lines++;
/* fall thru */
default:
tokc.tok = ch;
nextch();
break;
}
if (skip_mask & 1)
goto again;
}
void print_tok(FILE *f, JSToken *tt)
{
/* keep output lines in sync with input lines */
while (output_line_num < tt->line_num) {
putc('\n', f);
output_line_num++;
}
switch(tt->tok) {
case TOK_IDENT:
case TOK_COM:
case TOK_LCOM:
fprintf(f, "%s", tt->str);
break;
case TOK_NUM:
{
unsigned long a;
char *p;
a = strtoul(tt->str, &p, 0);
if (*p == '\0' && a <= 0x7fffffff) {
/* must be an integer */
fprintf(f, "%d", (int)a);
} else {
fprintf(f, "%s", tt->str);
}
}
break;
case TOK_STR3:
fprintf(f, "`%s`", tt->str);
break;
case TOK_STR1:
case TOK_STR2:
{
int i, c, quote_ch;
if (tt->tok == TOK_STR1)
quote_ch = '\'';
else
quote_ch = '\"';
fprintf(f, "%c", quote_ch);
for(i = 0; i < tt->len - 1; i++) {
c = (uint8_t)tt->str[i];
switch(c) {
case '\r':
fprintf(f, "\\r");
break;
case '\n':
fprintf(f, "\\n");
break;
case '\t':
fprintf(f, "\\t");
break;
case '\v':
fprintf(f, "\\v");
break;
case '\"':
case '\'':
if (c == quote_ch)
fprintf(f, "\\%c", c);
else
fprintf(f, "%c", c);
break;
case '\\':
fprintf(f, "\\\\");
break;
default:
/* XXX: no utf-8 support! */
if (c >= 32 && c <= 255) {
fprintf(f, "%c", c);
} else if (c <= 255)
fprintf(f, "\\x%02x", c);
else
fprintf(f, "\\u%04x", c);
break;
}
}
fprintf(f, "%c", quote_ch);
}
break;
default:
if (tokc.tok >= 256)
error("unsupported token in print_tok: %d", tt->tok);
fprintf(f, "%c", tt->tok);
break;
}
output_line_num += tt->lines;
}
/* check if token pasting could occur */
static BOOL compat_token(int c1, int c2)
{
if ((c1 == TOK_IDENT || c1 == TOK_NUM) &&
(c2 == TOK_IDENT || c2 == TOK_NUM))
return FALSE;
if ((c1 == c2 && strchr("+-<>&|=*/.", c1))
|| (c2 == '=' && strchr("+-<>&|!*/^%", c1))
|| (c1 == '=' && c2 == '>')
|| (c1 == '/' && c2 == '*')
|| (c1 == '.' && c2 == TOK_NUM)
|| (c1 == TOK_NUM && c2 == '.'))
return FALSE;
return TRUE;
}
void js_compress(const char *filename, const char *outfilename,
BOOL do_strip, BOOL keep_header)
{
FILE *outfile;
int ltok, seen_space;
line_num = 1;
infile = fopen(filename, "rb");
if (!infile) {
perror(filename);
exit(1);
}
output_line_num = 1;
outfile = fopen(outfilename, "wb");
if (!outfile) {
perror(outfilename);
exit(1);
}
nextch();
next();
ltok = 0;
seen_space = 0;
if (do_strip) {
if (keep_header) {
while (tokc.tok == ' ' ||
tokc.tok == '\n' ||
tokc.tok == '\t' ||
tokc.tok == '\v' ||
tokc.tok == '\b' ||
tokc.tok == '\f') {
seen_space = 1;
next();
}
if (tokc.tok == TOK_COM) {
print_tok(outfile, &tokc);
//fprintf(outfile, "\n");
ltok = tokc.tok;
seen_space = 0;
next();
}
}
for(;;) {
if (tokc.tok == TOK_EOF)
break;
if (tokc.tok == ' ' ||
tokc.tok == '\r' ||
tokc.tok == '\t' ||
tokc.tok == '\v' ||
tokc.tok == '\b' ||
tokc.tok == '\f' ||
tokc.tok == TOK_LCOM ||
tokc.tok == TOK_COM) {
/* don't print spaces or comments */
seen_space = 1;
} else if (tokc.tok == TOK_STR3) {
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
} else if (tokc.tok == TOK_STR1 || tokc.tok == TOK_STR2) {
int count, i;
/* find the optimal quote char */
count = 0;
for(i = 0; i < tokc.len; i++) {
if (tokc.str[i] == '\'')
count++;
else if (tokc.str[i] == '\"')
count--;
}
if (count > 0)
tokc.tok = TOK_STR2;
else if (count < 0)
tokc.tok = TOK_STR1;
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
} else {
if (seen_space && !compat_token(ltok, tokc.tok)) {
fprintf(outfile, " ");
}
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
}
next();
}
} else {
/* just handle preprocessing */
while (tokc.tok != TOK_EOF) {
print_tok(outfile, &tokc);
next();
}
}
fclose(outfile);
fclose(infile);
}
#define HASH_SIZE 30011
#define MATCH_LEN_MIN 3
#define MATCH_LEN_MAX (4 + 63)
#define DIST_MAX 65535
static int find_longest_match(int *pdist, const uint8_t *src, int src_len,
const int *hash_next, int cur_pos)
{
int pos, i, match_len, match_pos, pos_min, len_max;
len_max = min_int(src_len - cur_pos, MATCH_LEN_MAX);
match_len = 0;
match_pos = 0;
pos_min = max_int(cur_pos - DIST_MAX - 1, 0);
pos = hash_next[cur_pos];
while (pos >= pos_min) {
for(i = 0; i < len_max; i++) {
if (src[cur_pos + i] != src[pos + i])
break;
}
if (i > match_len) {
match_len = i;
match_pos = pos;
}
pos = hash_next[pos];
}
*pdist = cur_pos - match_pos - 1;
return match_len;
}
int lz_compress(uint8_t **pdst, const uint8_t *src, int src_len)
{
int *hash_table, *hash_next;
uint32_t h, v;
int i, dist, len, len1, dist1;
uint8_t *dst, *q;
/* build the hash table */
hash_table = malloc(sizeof(hash_table[0]) * HASH_SIZE);
for(i = 0; i < HASH_SIZE; i++)
hash_table[i] = -1;
hash_next = malloc(sizeof(hash_next[0]) * src_len);
for(i = 0; i < src_len; i++)
hash_next[i] = -1;
for(i = 0; i < src_len - MATCH_LEN_MIN + 1; i++) {
h = ((src[i] << 16) | (src[i + 1] << 8) | src[i + 2]) % HASH_SIZE;
hash_next[i] = hash_table[h];
hash_table[h] = i;
}
for(;i < src_len; i++) {
hash_next[i] = -1;
}
free(hash_table);
dst = malloc(src_len + 4); /* never larger than the source */
q = dst;
*q++ = src_len >> 24;
*q++ = src_len >> 16;
*q++ = src_len >> 8;
*q++ = src_len >> 0;
/* compress */
i = 0;
while (i < src_len) {
if (src[i] >= 128)
return -1;
len = find_longest_match(&dist, src, src_len, hash_next, i);
if (len >= MATCH_LEN_MIN) {
/* heuristic: see if better length just after */
len1 = find_longest_match(&dist1, src, src_len, hash_next, i + 1);
if (len1 > len)
goto no_match;
}
if (len < MATCH_LEN_MIN) {
no_match:
*q++ = src[i];
i++;
} else if (len <= (3 + 15) && dist < (1 << 10)) {
v = 0x8000 | ((len - 3) << 10) | dist;
*q++ = v >> 8;
*q++ = v;
i += len;
} else if (len >= 4 && len <= (4 + 63) && dist < (1 << 16)) {
v = 0xc00000 | ((len - 4) << 16) | dist;
*q++ = v >> 16;
*q++ = v >> 8;
*q++ = v;
i += len;
} else {
goto no_match;
}
}
free(hash_next);
*pdst = dst;
return q - dst;
}
static int load_file(uint8_t **pbuf, const char *filename)
{
FILE *f;
uint8_t *buf;
int buf_len;
f = fopen(filename, "rb");
if (!f) {
perror(filename);
exit(1);
}
fseek(f, 0, SEEK_END);
buf_len = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(buf_len + 1);
fread(buf, 1, buf_len, f);
buf[buf_len] = '\0';
fclose(f);
*pbuf = buf;
return buf_len;
}
static void save_file(const char *filename, const uint8_t *buf, int buf_len)
{
FILE *f;
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fwrite(buf, 1, buf_len, f);
fclose(f);
}
static void save_c_source(const char *filename, const uint8_t *buf, int buf_len,
const char *var_name)
{
FILE *f;
int i;
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fprintf(f, "/* This file is automatically generated - do not edit */\n\n");
fprintf(f, "const uint8_t %s[] = {\n", var_name);
for(i = 0; i < buf_len; i++) {
fprintf(f, " 0x%02x,", buf[i]);
if ((i % 8) == 7 || (i == buf_len - 1))
fprintf(f, "\n");
}
fprintf(f, "};\n");
fclose(f);
}
#define DEFAULT_OUTPUT_FILENAME "out.js"
void help(void)
{
printf("jscompress version 1.0 Copyright (c) 2008-2018 Fabrice Bellard\n"
"usage: jscompress [options] filename\n"
"Javascript compressor\n"
"\n"
"-h print this help\n"
"-n do not compress spaces\n"
"-H keep the first comment\n"
"-c compress to file\n"
"-C name compress to C source ('name' is the variable name)\n"
"-D symbol define preprocessor symbol\n"
"-U symbol undefine preprocessor symbol\n"
"-o outfile set the output filename (default=%s)\n",
DEFAULT_OUTPUT_FILENAME);
exit(1);
}
int main(int argc, char **argv)
{
int c, do_strip, keep_header, compress;
const char *out_filename, *c_var, *fname;
char tmpfilename[1024];
do_strip = 1;
keep_header = 0;
out_filename = DEFAULT_OUTPUT_FILENAME;
compress = 0;
c_var = NULL;
for(;;) {
c = getopt(argc, argv, "hno:HcC:D:U:");
if (c == -1)
break;
switch(c) {
case 'h':
help();
break;
case 'n':
do_strip = 0;
break;
case 'o':
out_filename = optarg;
break;
case 'H':
keep_header = 1;
break;
case 'c':
compress = 1;
break;
case 'C':
c_var = optarg;
compress = 1;
break;
case 'D':
define_symbol(optarg);
break;
case 'U':
undefine_symbol(optarg);
break;
}
}
if (optind >= argc)
help();
filename = argv[optind++];
if (compress) {
#if defined(__ANDROID__)
/* XXX: use another directory ? */
snprintf(tmpfilename, sizeof(tmpfilename), "out.%d", getpid());
#else
snprintf(tmpfilename, sizeof(tmpfilename), "/tmp/out.%d", getpid());
#endif
fname = tmpfilename;
} else {
fname = out_filename;
}
js_compress(filename, fname, do_strip, keep_header);
if (compress) {
uint8_t *buf1, *buf2;
int buf1_len, buf2_len;
buf1_len = load_file(&buf1, fname);
unlink(fname);
buf2_len = lz_compress(&buf2, buf1, buf1_len);
if (buf2_len < 0) {
fprintf(stderr, "Could not compress file (UTF8 chars are forbidden)\n");
exit(1);
}
if (c_var) {
save_c_source(out_filename, buf2, buf2_len, c_var);
} else {
save_file(out_filename, buf2, buf2_len);
}
free(buf1);
free(buf2);
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,350 @@
/*
* Tiny arbitrary precision floating point library
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifndef LIBBF_H
#define LIBBF_H
#include <stddef.h>
#include <stdint.h>
#if defined(__x86_64__)
#define LIMB_LOG2_BITS 6
#else
#define LIMB_LOG2_BITS 5
#endif
#define LIMB_BITS (1 << LIMB_LOG2_BITS)
#if LIMB_BITS == 64
typedef __int128 int128_t;
typedef unsigned __int128 uint128_t;
typedef int64_t slimb_t;
typedef uint64_t limb_t;
typedef uint128_t dlimb_t;
#define EXP_MIN INT64_MIN
#define EXP_MAX INT64_MAX
#else
typedef int32_t slimb_t;
typedef uint32_t limb_t;
typedef uint64_t dlimb_t;
#define EXP_MIN INT32_MIN
#define EXP_MAX INT32_MAX
#endif
/* in bits */
#define BF_EXP_BITS_MIN 3
#define BF_EXP_BITS_MAX (LIMB_BITS - 2)
#define BF_PREC_MIN 2
#define BF_PREC_MAX (((limb_t)1 << BF_EXP_BITS_MAX) - 2)
#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */
#if LIMB_BITS == 64
#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197))
#else
#define BF_CHKSUM_MOD 975620677U
#endif
#define BF_EXP_ZERO EXP_MIN
#define BF_EXP_INF (EXP_MAX - 1)
#define BF_EXP_NAN EXP_MAX
/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0,
+/-infinity is represented with expn = BF_EXP_INF and len = 0,
NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored)
*/
typedef struct {
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bf_t;
typedef enum {
BF_RNDN, /* round to nearest, ties to even */
BF_RNDZ, /* round to zero */
BF_RNDD, /* round to -inf */
BF_RNDU, /* round to +inf */
BF_RNDNA, /* round to nearest, ties away from zero */
BF_RNDNU, /* round to nearest, ties to +inf */
BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU,
inexact flag is always set) */
} bf_rnd_t;
/* allow subnormal numbers (only available if the number of exponent
bits is < BF_EXP_BITS_MAX and prec != BF_PREC_INF) */
#define BF_FLAG_SUBNORMAL (1 << 3)
#define BF_RND_MASK 0x7
#define BF_EXP_BITS_SHIFT 4
#define BF_EXP_BITS_MASK 0x3f
/* contains the rounding mode and number of exponents bits */
typedef uint32_t bf_flags_t;
typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size);
typedef struct {
bf_t val;
limb_t prec;
} BFConstCache;
typedef struct bf_context_t {
void *realloc_opaque;
bf_realloc_func_t *realloc_func;
BFConstCache log2_cache;
BFConstCache pi_cache;
struct BFNTTState *ntt_state;
} bf_context_t;
static inline int bf_get_exp_bits(bf_flags_t flags)
{
return BF_EXP_BITS_MAX - ((flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK);
}
static inline bf_flags_t bf_set_exp_bits(int n)
{
return (BF_EXP_BITS_MAX - n) << BF_EXP_BITS_SHIFT;
}
/* returned status */
#define BF_ST_INVALID_OP (1 << 0)
#define BF_ST_DIVIDE_ZERO (1 << 1)
#define BF_ST_OVERFLOW (1 << 2)
#define BF_ST_UNDERFLOW (1 << 3)
#define BF_ST_INEXACT (1 << 4)
/* not used yet, indicate that a memory allocation error occured. NaN
is returned */
#define BF_ST_MEM_ERROR (1 << 5)
#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */
static inline slimb_t bf_max(slimb_t a, slimb_t b)
{
if (a > b)
return a;
else
return b;
}
static inline slimb_t bf_min(slimb_t a, slimb_t b)
{
if (a < b)
return a;
else
return b;
}
void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
void *realloc_opaque);
void bf_context_end(bf_context_t *s);
/* free memory allocated for the bf cache data */
void bf_clear_cache(bf_context_t *s);
static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size)
{
return s->realloc_func(s->realloc_opaque, ptr, size);
}
void bf_init(bf_context_t *s, bf_t *r);
static inline void bf_delete(bf_t *r)
{
bf_context_t *s = r->ctx;
/* we accept to delete a zeroed bf_t structure */
if (s) {
bf_realloc(s, r->tab, 0);
}
}
static inline void bf_neg(bf_t *r)
{
r->sign ^= 1;
}
static inline int bf_is_finite(const bf_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bf_is_nan(const bf_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bf_is_zero(const bf_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
void bf_set_ui(bf_t *r, uint64_t a);
void bf_set_si(bf_t *r, int64_t a);
void bf_set_nan(bf_t *r);
void bf_set_zero(bf_t *r, int is_neg);
void bf_set_inf(bf_t *r, int is_neg);
void bf_set(bf_t *r, const bf_t *a);
void bf_move(bf_t *r, bf_t *a);
int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode);
void bf_set_float64(bf_t *a, double d);
int bf_cmpu(const bf_t *a, const bf_t *b);
int bf_cmp_full(const bf_t *a, const bf_t *b);
int bf_cmp_eq(const bf_t *a, const bf_t *b);
int bf_cmp_le(const bf_t *a, const bf_t *b);
int bf_cmp_lt(const bf_t *a, const bf_t *b);
int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags);
int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
#define BF_DIVREM_EUCLIDIAN BF_RNDF
int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bf_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags);
int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags);
int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags);
int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec,
bf_flags_t flags);
int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, limb_t prec, bf_flags_t flags);
int bf_rint(bf_t *r, limb_t prec, bf_flags_t flags);
int bf_round(bf_t *r, limb_t prec, bf_flags_t flags);
int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a);
int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
slimb_t bf_get_exp_min(const bf_t *a);
void bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b);
void bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b);
void bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b);
/* additional flags for bf_atof */
/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */
#define BF_ATOF_NO_HEX (1 << 16)
/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */
#define BF_ATOF_BIN_OCT (1 << 17)
/* Only accept integers (no decimal point, no exponent, no infinity nor NaN */
#define BF_ATOF_INT_ONLY (1 << 18)
/* Do not accept radix prefix after sign */
#define BF_ATOF_NO_PREFIX_AFTER_SIGN (1 << 19)
/* Do not parse NaN and parse case sensitive 'Infinity' */
#define BF_ATOF_JS_QUIRKS (1 << 20)
/* Do not round integers to the indicated precision */
#define BF_ATOF_INT_PREC_INF (1 << 21)
/* Support legacy octal syntax for well formed numbers */
#define BF_ATOF_LEGACY_OCTAL (1 << 22)
/* accept _ between digits as a digit separator */
#define BF_ATOF_UNDERSCORE_SEP (1 << 23)
/* if a 'n' suffix is present, force integer parsing (XXX: remove) */
#define BF_ATOF_INT_N_SUFFIX (1 << 24)
/* if set return NaN if empty number string (instead of 0) */
#define BF_ATOF_NAN_IF_EMPTY (1 << 25)
/* only accept decimal floating point if radix = 0 */
#define BF_ATOF_ONLY_DEC_FLOAT (1 << 26)
/* one more return flag: indicate that the parsed number is an integer
(only set when the flags BF_ATOF_INT_PREC_INF or
BF_ATOF_INT_N_SUFFIX are used) */
#define BF_ATOF_ST_INTEGER (1 << 5)
int bf_atof(bf_t *a, const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
/* this version accepts prec = BF_PREC_INF and returns the radix
exponent */
int bf_atof2(bf_t *r, slimb_t *pexponent,
const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
slimb_t expn, limb_t prec, bf_flags_t flags);
#define BF_FTOA_FORMAT_MASK (3 << 16)
/* fixed format: prec significant digits rounded with (flags &
BF_RND_MASK). Exponential notation is used if too many zeros are
needed. */
#define BF_FTOA_FORMAT_FIXED (0 << 16)
/* fractional format: prec digits after the decimal point rounded with
(flags & BF_RND_MASK) */
#define BF_FTOA_FORMAT_FRAC (1 << 16)
/* free format: use as many digits as necessary so that bf_atof()
return the same number when using precision 'prec', rounding to
nearest and the subnormal+exponent configuration of 'flags'. The
result is meaningful only if 'a' is already rounded to the wanted
precision.
Infinite precision (BF_PREC_INF) is supported when the radix is a
power of two. */
#define BF_FTOA_FORMAT_FREE (2 << 16)
/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits
(takes more computation time). */
#define BF_FTOA_FORMAT_FREE_MIN (3 << 16)
/* force exponential notation for fixed or free format */
#define BF_FTOA_FORCE_EXP (1 << 20)
/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for
base 2 if non zero value */
#define BF_FTOA_ADD_PREFIX (1 << 21)
#define BF_FTOA_JS_QUIRKS (1 << 22)
size_t bf_ftoa(char **pbuf, const bf_t *a, int radix, limb_t prec,
bf_flags_t flags);
/* modulo 2^n instead of saturation. NaN and infinity return 0 */
#define BF_GET_INT_MOD (1 << 0)
int bf_get_int32(int *pres, const bf_t *a, int flags);
int bf_get_int64(int64_t *pres, const bf_t *a, int flags);
/* the following functions are exported for testing only. */
void bf_print_str(const char *str, const bf_t *a);
void bf_resize(bf_t *r, limb_t len);
int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len);
void bf_recip(bf_t *r, const bf_t *a, limb_t prec);
void bf_rsqrt(bf_t *a, const bf_t *x, limb_t prec);
int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags);
int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k);
slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
int is_ceil1);
/* transcendental functions */
int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
#define BF_POW_JS_QUICKS (1 << 16)
int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags);
int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,
limb_t prec, bf_flags_t flags);
int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
#endif /* LIBBF_H */

View File

@ -0,0 +1,58 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifdef DEF
DEF(invalid, 1) /* never used */
DEF(char, 3)
DEF(char32, 5)
DEF(dot, 1)
DEF(any, 1) /* same as dot but match any character including line terminator */
DEF(line_start, 1)
DEF(line_end, 1)
DEF(goto, 5)
DEF(split_goto_first, 5)
DEF(split_next_first, 5)
DEF(match, 1)
DEF(save_start, 2) /* save start position */
DEF(save_end, 2) /* save end position, must come after saved_start */
DEF(save_reset, 3) /* reset save positions */
DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
DEF(push_i32, 5) /* push integer on the stack */
DEF(drop, 1)
DEF(word_boundary, 1)
DEF(not_word_boundary, 1)
DEF(back_reference, 2)
DEF(backward_back_reference, 2) /* must come after back_reference */
DEF(range, 3) /* variable length */
DEF(range32, 3) /* variable length */
DEF(lookahead, 5)
DEF(negative_lookahead, 5)
DEF(push_char_pos, 1) /* push the character position on the stack */
DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character
position */
DEF(prev, 1) /* go to the previous char */
DEF(simple_greedy_quant, 17)
#endif /* DEF */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifndef LIBREGEXP_H
#define LIBREGEXP_H
#include <stddef.h>
#include "libunicode.h"
#define LRE_BOOL int /* for documentation purposes */
#define LRE_FLAG_GLOBAL (1 << 0)
#define LRE_FLAG_IGNORECASE (1 << 1)
#define LRE_FLAG_MULTILINE (1 << 2)
#define LRE_FLAG_DOTALL (1 << 3)
#define LRE_FLAG_UTF16 (1 << 4)
#define LRE_FLAG_STICKY (1 << 5)
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
const char *buf, size_t buf_len, int re_flags,
void *opaque);
int lre_get_capture_count(const uint8_t *bc_buf);
int lre_get_flags(const uint8_t *bc_buf);
int lre_exec(uint8_t **capture,
const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
int cbuf_type, void *opaque);
int lre_parse_escape(const uint8_t **pp, int allow_utf16);
LRE_BOOL lre_is_space(int c);
/* must be provided by the user */
LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size);
void *lre_realloc(void *opaque, void *ptr, size_t size);
/* JS identifier test */
extern uint32_t const lre_id_start_table_ascii[4];
extern uint32_t const lre_id_continue_table_ascii[4];
static inline int lre_js_is_ident_first(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space(c);
#endif
}
}
static inline int lre_js_is_ident_next(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c) || c == 0x200C || c == 0x200D;
#else
return !lre_is_space(c) || c == 0x200C || c == 0x200D;
#endif
}
}
#undef LRE_BOOL
#endif /* LIBREGEXP_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
/*
* Unicode utilities
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifndef LIBUNICODE_H
#define LIBUNICODE_H
#include <inttypes.h>
#define LRE_BOOL int /* for documentation purposes */
/* define it to include all the unicode tables (40KB larger) */
#define CONFIG_ALL_UNICODE
#define LRE_CC_RES_LEN_MAX 3
typedef enum {
UNICODE_NFC,
UNICODE_NFD,
UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
LRE_BOOL lre_is_cased(uint32_t c);
LRE_BOOL lre_is_case_ignorable(uint32_t c);
/* char ranges */
typedef struct {
int len; /* in points, always even */
int size;
uint32_t *points; /* points sorted by increasing value */
void *mem_opaque;
void *(*realloc_func)(void *opaque, void *ptr, size_t size);
} CharRange;
typedef enum {
CR_OP_UNION,
CR_OP_INTER,
CR_OP_XOR,
} CharRangeOpEnum;
void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
void cr_free(CharRange *cr);
int cr_realloc(CharRange *cr, int size);
int cr_copy(CharRange *cr, const CharRange *cr1);
static inline int cr_add_point(CharRange *cr, uint32_t v)
{
if (cr->len >= cr->size) {
if (cr_realloc(cr, cr->len + 1))
return -1;
}
cr->points[cr->len++] = v;
return 0;
}
static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
if ((cr->len + 2) > cr->size) {
if (cr_realloc(cr, cr->len + 2))
return -1;
}
cr->points[cr->len++] = c1;
cr->points[cr->len++] = c2;
return 0;
}
int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len);
static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
uint32_t b_pt[2];
b_pt[0] = c1;
b_pt[1] = c2 + 1;
return cr_union1(cr, b_pt, 2);
}
int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
const uint32_t *b_pt, int b_len, int op);
int cr_invert(CharRange *cr);
#ifdef CONFIG_ALL_UNICODE
LRE_BOOL lre_is_id_start(uint32_t c);
LRE_BOOL lre_is_id_continue(uint32_t c);
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
UnicodeNormalizationEnum n_type,
void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
/* Unicode character range functions */
int unicode_script(CharRange *cr,
const char *script_name, LRE_BOOL is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name);
int unicode_prop(CharRange *cr, const char *prop_name);
#endif /* CONFIG_ALL_UNICODE */
#undef LRE_BOOL
#endif /* LIBUNICODE_H */

View File

@ -0,0 +1,100 @@
/*
* Linux klist like system
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* 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.
*/
#ifndef LIST_H
#define LIST_H
#ifndef NULL
#include <stddef.h>
#endif
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_HEAD_INIT(el) { &(el), &(el) }
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) \
((type *)((uint8_t *)(el) - offsetof(type, member)))
static inline void init_list_head(struct list_head *head)
{
head->prev = head;
head->next = head;
}
/* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next)
{
prev->next = el;
el->prev = prev;
el->next = next;
next->prev = el;
}
/* add 'el' at the head of the list 'head' (= after element head) */
static inline void list_add(struct list_head *el, struct list_head *head)
{
__list_add(el, head, head->next);
}
/* add 'el' at the end of the list 'head' (= before element head) */
static inline void list_add_tail(struct list_head *el, struct list_head *head)
{
__list_add(el, head->prev, head);
}
static inline void list_del(struct list_head *el)
{
struct list_head *prev, *next;
prev = el->prev;
next = el->next;
prev->next = next;
next->prev = prev;
el->prev = NULL; /* fail safe */
el->next = NULL; /* fail safe */
}
static inline int list_empty(struct list_head *el)
{
return el->next == el;
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)
#define list_for_each_safe(el, el1, head) \
for(el = (head)->next, el1 = el->next; el != (head); \
el = el1, el1 = el->next)
#define list_for_each_prev(el, head) \
for(el = (head)->prev; el != (head); el = el->prev)
#define list_for_each_prev_safe(el, el1, head) \
for(el = (head)->prev, el1 = el->prev; el != (head); \
el = el1, el1 = el->prev)
#endif /* LIST_H */

View File

@ -0,0 +1,466 @@
/*
* QuickJS stand alone interpreter
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#if defined(__APPLE__)
#include <malloc/malloc.h>
#elif defined(__linux__)
#include <malloc.h>
#endif
#include "cutils.h"
#include "quickjs-libc.h"
extern const uint8_t repl[];
extern const uint32_t repl_size;
#ifdef CONFIG_BIGNUM
extern const uint8_t qjscalc[];
extern const uint32_t qjscalc_size;
#endif
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
const char *filename, int eval_flags)
{
JSValue val;
int ret;
val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
if (JS_IsException(val)) {
js_std_dump_error(ctx);
ret = -1;
} else {
ret = 0;
}
JS_FreeValue(ctx, val);
return ret;
}
static int eval_file(JSContext *ctx, const char *filename, int eval_flags)
{
uint8_t *buf;
int ret;
size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename);
if (!buf) {
perror(filename);
exit(1);
}
ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
js_free(ctx, buf);
return ret;
}
#if defined(__APPLE__)
#define MALLOC_OVERHEAD 0
#else
#define MALLOC_OVERHEAD 8
#endif
struct trace_malloc_data {
uint8_t *base;
};
static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr,
struct trace_malloc_data *dp)
{
return ptr - dp->base;
}
/* default memory allocation functions with memory limitation */
static inline size_t js_trace_malloc_usable_size(void *ptr)
{
#if defined(__APPLE__)
return malloc_size(ptr);
#elif defined(_WIN32)
return _msize(ptr);
#elif defined(EMSCRIPTEN)
return 0;
#elif defined(__linux__)
return malloc_usable_size(ptr);
#else
/* change this to `return 0;` if compilation fails */
return malloc_usable_size(ptr);
#endif
}
static void __attribute__((format(printf, 2, 3)))
js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
{
va_list ap;
int c;
va_start(ap, fmt);
while ((c = *fmt++) != '\0') {
if (c == '%') {
/* only handle %p and %zd */
if (*fmt == 'p') {
uint8_t *ptr = va_arg(ap, void *);
if (ptr == NULL) {
printf("NULL");
} else {
printf("H%+06lld.%zd",
js_trace_malloc_ptr_offset(ptr, s->opaque),
js_trace_malloc_usable_size(ptr));
}
fmt++;
continue;
}
if (fmt[0] == 'z' && fmt[1] == 'd') {
size_t sz = va_arg(ap, size_t);
printf("%zd", sz);
fmt += 2;
continue;
}
}
putc(c, stdout);
}
va_end(ap);
}
static void js_trace_malloc_init(struct trace_malloc_data *s)
{
free(s->base = malloc(8));
}
static void *js_trace_malloc(JSMallocState *s, size_t size)
{
void *ptr;
/* Do not allocate zero bytes: behavior is platform dependent */
assert(size != 0);
if (unlikely(s->malloc_size + size > s->malloc_limit))
return NULL;
ptr = malloc(size);
js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
if (ptr) {
s->malloc_count++;
s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
}
return ptr;
}
static void js_trace_free(JSMallocState *s, void *ptr)
{
if (!ptr)
return;
js_trace_malloc_printf(s, "F %p\n", ptr);
s->malloc_count--;
s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
free(ptr);
}
static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
{
size_t old_size;
if (!ptr) {
if (size == 0)
return NULL;
return js_trace_malloc(s, size);
}
old_size = js_trace_malloc_usable_size(ptr);
if (size == 0) {
js_trace_malloc_printf(s, "R %zd %p\n", size, ptr);
s->malloc_count--;
s->malloc_size -= old_size + MALLOC_OVERHEAD;
free(ptr);
return NULL;
}
if (s->malloc_size + size - old_size > s->malloc_limit)
return NULL;
js_trace_malloc_printf(s, "R %zd %p", size, ptr);
ptr = realloc(ptr, size);
js_trace_malloc_printf(s, " -> %p\n", ptr);
if (ptr) {
s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size;
}
return ptr;
}
static const JSMallocFunctions trace_mf = {
js_trace_malloc,
js_trace_free,
js_trace_realloc,
#if defined(__APPLE__)
malloc_size,
#elif defined(_WIN32)
(size_t (*)(const void *))_msize,
#elif defined(EMSCRIPTEN)
NULL,
#elif defined(__linux__)
(size_t (*)(const void *))malloc_usable_size,
#else
/* change this to `NULL,` if compilation fails */
malloc_usable_size,
#endif
};
#ifdef CONFIG_BIGNUM
#define PROG_NAME "qjsbn"
#else
#define PROG_NAME "qjs"
#endif
void help(void)
{
printf("QuickJS version " CONFIG_VERSION "\n"
"usage: " PROG_NAME " [options] [files]\n"
"-h --help list options\n"
"-e --eval EXPR evaluate EXPR\n"
"-i --interactive go to interactive mode\n"
"-m --module load as ES6 module (default if .mjs file extension)\n"
#ifdef CONFIG_BIGNUM
" --qjscalc load the QJSCalc runtime (default if invoked as qjscalc)\n"
#endif
"-T --trace trace memory allocation\n"
"-d --dump dump the memory usage stats\n"
"-q --quit just instantiate the interpreter and quit\n");
exit(1);
}
int main(int argc, char **argv)
{
JSRuntime *rt;
JSContext *ctx;
struct trace_malloc_data trace_data = { NULL };
int optind;
char *expr = NULL;
int interactive = 0;
int dump_memory = 0;
int trace_memory = 0;
int empty_run = 0;
int module = 0;
int load_std = 1;
#ifdef CONFIG_BIGNUM
int load_jscalc;
#endif
#ifdef CONFIG_BIGNUM
/* load jscalc runtime if invoked as 'qjscalc' */
{
const char *p, *exename;
exename = argv[0];
p = strrchr(exename, '/');
if (p)
exename = p + 1;
load_jscalc = !strcmp(exename, "qjscalc");
}
#endif
/* cannot use getopt because we want to pass the command line to
the script */
optind = 1;
while (optind < argc && *argv[optind] == '-') {
char *arg = argv[optind] + 1;
const char *longopt = "";
/* a single - is not an option, it also stops argument scanning */
if (!*arg)
break;
optind++;
if (*arg == '-') {
longopt = arg + 1;
arg += strlen(arg);
/* -- stops argument scanning */
if (!*longopt)
break;
}
for (; *arg || *longopt; longopt = "") {
char opt = *arg;
if (opt)
arg++;
if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) {
help();
continue;
}
if (opt == 'e' || !strcmp(longopt, "eval")) {
if (*arg) {
expr = arg;
break;
}
if (optind < argc) {
expr = argv[optind++];
break;
}
fprintf(stderr, "qjs: missing expression for -e\n");
exit(2);
}
if (opt == 'i' || !strcmp(longopt, "interactive")) {
interactive++;
continue;
}
if (opt == 'm' || !strcmp(longopt, "module")) {
module = 1;
continue;
}
if (opt == 'd' || !strcmp(longopt, "dump")) {
dump_memory++;
continue;
}
if (opt == 'T' || !strcmp(longopt, "trace")) {
trace_memory++;
continue;
}
if (!strcmp(longopt, "nostd")) {
load_std = 0;
continue;
}
#ifdef CONFIG_BIGNUM
if (!strcmp(longopt, "qjscalc")) {
load_jscalc = 1;
continue;
}
#endif
if (opt == 'q' || !strcmp(longopt, "quit")) {
empty_run++;
continue;
}
if (opt) {
fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
} else {
fprintf(stderr, "qjs: unknown option '--%s'\n", longopt);
}
help();
}
}
if (trace_memory) {
js_trace_malloc_init(&trace_data);
rt = JS_NewRuntime2(&trace_mf, &trace_data);
} else {
rt = JS_NewRuntime();
}
if (!rt) {
fprintf(stderr, "qjs: cannot allocate JS runtime\n");
exit(2);
}
ctx = JS_NewContext(rt);
if (!ctx) {
fprintf(stderr, "qjs: cannot allocate JS context\n");
exit(2);
}
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
if (!empty_run) {
#ifdef CONFIG_BIGNUM
if (load_jscalc) {
js_std_eval_binary(ctx, qjscalc, qjscalc_size, 0);
}
#endif
js_std_add_helpers(ctx, argc - optind, argv + optind);
/* system modules */
js_init_module_std(ctx, "std");
js_init_module_os(ctx, "os");
/* make 'std' and 'os' visible to non module code */
if (load_std) {
const char *str = "import * as std from 'std';\n"
"import * as os from 'os';\n"
"std.global.std = std;\n"
"std.global.os = os;\n";
eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
}
if (expr) {
if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
goto fail;
} else
if (optind >= argc) {
/* interactive mode */
interactive = 1;
} else {
int eval_flags;
const char *filename;
filename = argv[optind];
if (module || has_suffix(filename, ".mjs"))
eval_flags = JS_EVAL_TYPE_MODULE;
else
eval_flags = JS_EVAL_TYPE_GLOBAL;
eval_flags |= JS_EVAL_FLAG_SHEBANG;
if (eval_file(ctx, filename, eval_flags))
goto fail;
}
if (interactive) {
js_std_eval_binary(ctx, repl, repl_size, 0);
}
js_std_loop(ctx);
}
if (dump_memory) {
JSMemoryUsage stats;
JS_ComputeMemoryUsage(rt, &stats);
JS_DumpMemoryUsage(stdout, &stats, rt);
}
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
if (empty_run && dump_memory) {
clock_t t[5];
double best[5];
int i, j;
for (i = 0; i < 100; i++) {
t[0] = clock();
rt = JS_NewRuntime();
t[1] = clock();
ctx = JS_NewContext(rt);
t[2] = clock();
JS_FreeContext(ctx);
t[3] = clock();
JS_FreeRuntime(rt);
t[4] = clock();
for (j = 4; j > 0; j--) {
double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC;
if (i == 0 || best[j] > ms)
best[j] = ms;
}
}
printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n",
best[1] + best[2] + best[3] + best[4],
best[1], best[2], best[3], best[4]);
}
return 0;
fail:
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 1;
}

View File

@ -0,0 +1,636 @@
/*
* QuickJS command line compiler
*
* Copyright (c) 2018-2019 Fabrice Bellard
*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#if !defined(_WIN32)
#include <sys/wait.h>
#endif
#include "cutils.h"
#include "quickjs-libc.h"
typedef struct {
char *name;
char *short_name;
int flags;
} namelist_entry_t;
typedef struct namelist_t {
namelist_entry_t *array;
int count;
int size;
} namelist_t;
typedef struct {
const char *option_name;
const char *init_name;
} FeatureEntry;
#define FE_ALL (-1)
static namelist_t cname_list;
static namelist_t cmodule_list;
static namelist_t init_module_list;
static uint64_t feature_bitmap;
static FILE *outfile;
static BOOL byte_swap;
static const FeatureEntry feature_list[] = {
{ "eval", "Eval" },
{ "string-normalize", "StringNormalize" },
{ "regexp", "RegExp" },
{ "json", "JSON" },
{ "proxy", "Proxy" },
{ "map", "MapSet" },
{ "typedarray", "TypedArrays" },
{ "promise", "Promise" },
};
void namelist_add(namelist_t *lp, const char *name, const char *short_name,
int flags)
{
namelist_entry_t *e;
if (lp->count == lp->size) {
size_t newsize = lp->size + (lp->size >> 1) + 4;
namelist_entry_t *a =
realloc(lp->array, sizeof(lp->array[0]) * newsize);
/* XXX: check for realloc failure */
lp->array = a;
lp->size = newsize;
}
e = &lp->array[lp->count++];
e->name = strdup(name);
if (short_name)
e->short_name = strdup(short_name);
else
e->short_name = NULL;
e->flags = flags;
}
void namelist_free(namelist_t *lp)
{
while (lp->count > 0) {
namelist_entry_t *e = &lp->array[--lp->count];
free(e->name);
free(e->short_name);
}
free(lp->array);
lp->array = NULL;
lp->size = 0;
}
namelist_entry_t *namelist_find(namelist_t *lp, const char *name)
{
int i;
for(i = 0; i < lp->count; i++) {
namelist_entry_t *e = &lp->array[i];
if (!strcmp(e->name, name))
return e;
}
return NULL;
}
static void get_c_name(char *buf, int buf_size, const char *file)
{
const char *p, *r;
size_t len;
p = strrchr(file, '/');
if (!p)
p = file;
else
p++;
r = strrchr(p, '.');
if (!r)
r = p + strlen(p);
len = r - p;
if (len > buf_size - 1)
len = buf_size - 1;
memcpy(buf, p, len);
buf[len] = '\0';
}
static void dump_hex(FILE *f, const uint8_t *buf, size_t len)
{
size_t i, col;
col = 0;
for(i = 0; i < len; i++) {
fprintf(f, " 0x%02x,", buf[i]);
if (++col == 8) {
fprintf(f, "\n");
col = 0;
}
}
if (col != 0)
fprintf(f, "\n");
}
static void output_object_code(JSContext *ctx,
FILE *fo, JSValueConst obj, const char *c_name,
BOOL load_only)
{
uint8_t *out_buf;
size_t out_buf_len;
int flags;
flags = JS_WRITE_OBJ_BYTECODE;
if (byte_swap)
flags |= JS_WRITE_OBJ_BSWAP;
out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags);
if (!out_buf) {
js_std_dump_error(ctx);
exit(1);
}
namelist_add(&cname_list, c_name, NULL, load_only);
fprintf(fo, "const uint32_t %s_size = %u;\n\n",
c_name, (unsigned int)out_buf_len);
fprintf(fo, "const uint8_t %s[%u] = {\n",
c_name, (unsigned int)out_buf_len);
dump_hex(fo, out_buf, out_buf_len);
fprintf(fo, "};\n\n");
js_free(ctx, out_buf);
}
static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m)
{
/* should never be called when compiling JS code */
abort();
}
JSModuleDef *jsc_module_loader(JSContext *ctx,
const char *module_name, void *opaque)
{
JSModuleDef *m;
namelist_entry_t *e;
/* check if it is a declared C or system module */
e = namelist_find(&cmodule_list, module_name);
if (e) {
/* add in the static init module list */
namelist_add(&init_module_list, e->name, e->short_name, 0);
/* create a dummy module */
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
} else if (has_suffix(module_name, ".so")) {
fprintf(stderr, "Warning: binary module '%s' is not compiled\n", module_name);
/* create a dummy module */
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
} else {
size_t buf_len;
uint8_t *buf;
JSValue func_val;
char cname[1024];
buf = js_load_file(ctx, &buf_len, module_name);
if (!buf) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
module_name);
return NULL;
}
/* compile the module */
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
js_free(ctx, buf);
if (JS_IsException(func_val))
return NULL;
get_c_name(cname, sizeof(cname), module_name);
output_object_code(ctx, outfile, func_val, cname, TRUE);
/* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
}
return m;
}
static void compile_file(JSContext *ctx, FILE *fo,
const char *filename,
const char *c_name1,
BOOL is_module)
{
uint8_t *buf;
char c_name[1024];
int eval_flags;
JSValue obj;
size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename);
if (!buf) {
fprintf(stderr, "Could not load '%s'\n", filename);
exit(1);
}
eval_flags = JS_EVAL_FLAG_SHEBANG | JS_EVAL_FLAG_COMPILE_ONLY;
if (is_module)
eval_flags |= JS_EVAL_TYPE_MODULE;
else
eval_flags |= JS_EVAL_TYPE_GLOBAL;
obj = JS_Eval(ctx, (char *)buf, buf_len, filename, eval_flags);
if (JS_IsException(obj)) {
js_std_dump_error(ctx);
exit(1);
}
js_free(ctx, buf);
if (c_name1) {
pstrcpy(c_name, sizeof(c_name), c_name1);
} else {
get_c_name(c_name, sizeof(c_name), filename);
}
output_object_code(ctx, fo, obj, c_name, FALSE);
JS_FreeValue(ctx, obj);
}
static const char main_c_template1[] =
"int main(int argc, char **argv)\n"
"{\n"
" JSRuntime *rt;\n"
" JSContext *ctx;\n"
" rt = JS_NewRuntime();\n"
;
static const char main_c_template2[] =
" js_std_loop(ctx);\n"
" JS_FreeContext(ctx);\n"
" JS_FreeRuntime(rt);\n"
" return 0;\n"
"}\n";
#ifdef CONFIG_BIGNUM
#define PROG_NAME "qjscbn"
#else
#define PROG_NAME "qjsc"
#endif
void help(void)
{
printf("QuickJS Compiler version " CONFIG_VERSION "\n"
"usage: " PROG_NAME " [options] [files]\n"
"\n"
"options are:\n"
"-c only output bytecode in a C file\n"
"-e output main() and bytecode in a C file (default = executable output)\n"
"-o output set the output filename\n"
"-N cname set the C name of the generated data\n"
"-m compile as Javascript module\n"
"-M module_name[,cname] add initialization code for an external C module\n"
"-x byte swapped output\n"
);
#ifdef CONFIG_LTO
{
int i;
printf("-flto use link time optimization\n");
printf("-fno-[");
for(i = 0; i < countof(feature_list); i++) {
if (i != 0)
printf("|");
printf("%s", feature_list[i].option_name);
}
printf("]\n"
" disable selected language features (smaller code size)\n");
}
#endif
exit(1);
}
#if defined(CONFIG_CC) && !defined(_WIN32)
int exec_cmd(char **argv)
{
int pid, status, ret;
pid = fork();
if (pid == 0) {
execvp(argv[0], argv);
exit(1);
}
for(;;) {
ret = waitpid(pid, &status, 0);
if (ret == pid && WIFEXITED(status))
break;
}
return WEXITSTATUS(status);
}
static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
const char *argv[64];
const char **arg, *bn_suffix, *lto_suffix;
char libjsname[1024];
char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p;
int ret;
/* get the directory of the executable */
pstrcpy(exe_dir, sizeof(exe_dir), exename);
p = strrchr(exe_dir, '/');
if (p) {
*p = '\0';
} else {
pstrcpy(exe_dir, sizeof(exe_dir), ".");
}
/* if 'quickjs.h' is present at the same path as the executable, we
use it as include and lib directory */
snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir);
if (access(buf, R_OK) == 0) {
pstrcpy(inc_dir, sizeof(inc_dir), exe_dir);
pstrcpy(lib_dir, sizeof(lib_dir), exe_dir);
} else {
snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX);
snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX);
}
lto_suffix = "";
#ifdef CONFIG_BIGNUM
bn_suffix = ".bn";
#else
bn_suffix = "";
#endif
arg = argv;
*arg++ = CONFIG_CC;
*arg++ = "-O2";
#ifdef CONFIG_LTO
if (use_lto) {
*arg++ = "-flto";
lto_suffix = ".lto";
}
#endif
/* XXX: use the executable path to find the includes files and
libraries */
*arg++ = "-D";
*arg++ = "_GNU_SOURCE";
*arg++ = "-I";
*arg++ = inc_dir;
*arg++ = "-o";
*arg++ = out_filename;
*arg++ = cfilename;
snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a",
lib_dir, bn_suffix, lto_suffix);
*arg++ = libjsname;
*arg++ = "-lm";
*arg++ = "-ldl";
*arg = NULL;
if (verbose) {
for(arg = argv; *arg != NULL; arg++)
printf("%s ", *arg);
printf("\n");
}
ret = exec_cmd((char **)argv);
unlink(cfilename);
return ret;
}
#else
static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
fprintf(stderr, "Executable output is not supported for this target\n");
exit(1);
return 0;
}
#endif
typedef enum {
OUTPUT_C,
OUTPUT_C_MAIN,
OUTPUT_EXECUTABLE,
} OutputTypeEnum;
int main(int argc, char **argv)
{
int c, i, verbose;
const char *out_filename, *cname;
char cfilename[1024];
FILE *fo;
JSRuntime *rt;
JSContext *ctx;
BOOL module, use_lto;
OutputTypeEnum output_type;
out_filename = NULL;
output_type = OUTPUT_EXECUTABLE;
cname = NULL;
feature_bitmap = FE_ALL;
module = FALSE;
byte_swap = FALSE;
verbose = 0;
use_lto = FALSE;
/* add system modules */
namelist_add(&cmodule_list, "std", "std", 0);
namelist_add(&cmodule_list, "os", "os", 0);
for(;;) {
c = getopt(argc, argv, "ho:cN:f:mxevM:");
if (c == -1)
break;
switch(c) {
case 'h':
help();
case 'o':
out_filename = optarg;
break;
case 'c':
output_type = OUTPUT_C;
break;
case 'e':
output_type = OUTPUT_C_MAIN;
break;
case 'N':
cname = optarg;
break;
case 'f':
{
const char *p;
p = optarg;
if (!strcmp(optarg, "lto")) {
use_lto = TRUE;
} else if (strstart(p, "no-", &p)) {
use_lto = TRUE;
for(i = 0; i < countof(feature_list); i++) {
if (!strcmp(p, feature_list[i].option_name)) {
feature_bitmap &= ~((uint64_t)1 << i);
break;
}
}
if (i == countof(feature_list))
goto bad_feature;
} else {
bad_feature:
fprintf(stderr, "unsupported feature: %s\n", optarg);
exit(1);
}
}
break;
case 'm':
module = TRUE;
break;
case 'M':
{
char *p;
char path[1024];
char cname[1024];
pstrcpy(path, sizeof(path), optarg);
p = strchr(path, ',');
if (p) {
*p = '\0';
pstrcpy(cname, sizeof(cname), p + 1);
} else {
get_c_name(cname, sizeof(cname), path);
}
namelist_add(&cmodule_list, path, cname, 0);
}
break;
case 'x':
byte_swap = TRUE;
break;
case 'v':
verbose++;
break;
default:
break;
}
}
if (optind >= argc)
help();
if (!out_filename) {
if (output_type == OUTPUT_EXECUTABLE) {
out_filename = "a.out";
} else {
out_filename = "out.c";
}
}
if (output_type == OUTPUT_EXECUTABLE) {
#if defined(_WIN32) || defined(__ANDROID__)
/* XXX: find a /tmp directory ? */
snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid());
#else
snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid());
#endif
} else {
pstrcpy(cfilename, sizeof(cfilename), out_filename);
}
fo = fopen(cfilename, "w");
if (!fo) {
perror(cfilename);
exit(1);
}
outfile = fo;
rt = JS_NewRuntime();
ctx = JS_NewContextRaw(rt);
JS_AddIntrinsicEval(ctx);
JS_AddIntrinsicRegExpCompiler(ctx);
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
"\n"
);
if (output_type != OUTPUT_C) {
fprintf(fo, "#include \"quickjs-libc.h\"\n"
"\n"
);
} else {
fprintf(fo, "#include <inttypes.h>\n"
"\n"
);
}
for(i = optind; i < argc; i++) {
const char *filename = argv[i];
BOOL module1 = module || has_suffix(filename, ".mjs");
compile_file(ctx, fo, filename, cname, module1);
cname = NULL;
}
if (output_type != OUTPUT_C) {
fputs(main_c_template1, fo);
fprintf(fo, " ctx = JS_NewContextRaw(rt);\n");
/* add the basic objects */
fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n");
for(i = 0; i < countof(feature_list); i++) {
if (feature_bitmap & ((uint64_t)1 << i)) {
fprintf(fo, " JS_AddIntrinsic%s(ctx);\n",
feature_list[i].init_name);
}
}
fprintf(fo, " js_std_add_helpers(ctx, argc, argv);\n");
for(i = 0; i < init_module_list.count; i++) {
namelist_entry_t *e = &init_module_list.array[i];
/* initialize the static C modules */
fprintf(fo,
" {\n"
" extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n"
" js_init_module_%s(ctx, \"%s\");\n"
" }\n",
e->short_name, e->short_name, e->name);
}
for(i = 0; i < cname_list.count; i++) {
namelist_entry_t *e = &cname_list.array[i];
fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, %s);\n",
e->name, e->name,
e->flags ? "JS_EVAL_BINARY_LOAD_ONLY" : "0");
}
fputs(main_c_template2, fo);
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
fclose(fo);
if (output_type == OUTPUT_EXECUTABLE) {
return output_executable(out_filename, cfilename, use_lto, verbose,
argv[0]);
}
namelist_free(&cname_list);
namelist_free(&cmodule_list);
namelist_free(&init_module_list);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,274 @@
/*
* QuickJS atom definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* 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.
*/
#ifdef DEF
/* Note: first atoms are considered as keywords in the parser */
DEF(null, "null") /* must be first */
DEF(false, "false")
DEF(true, "true")
DEF(if, "if")
DEF(else, "else")
DEF(return, "return")
DEF(var, "var")
DEF(this, "this")
DEF(delete, "delete")
DEF(void, "void")
DEF(typeof, "typeof")
DEF(new, "new")
DEF(in, "in")
DEF(instanceof, "instanceof")
DEF(do, "do")
DEF(while, "while")
DEF(for, "for")
DEF(break, "break")
DEF(continue, "continue")
DEF(switch, "switch")
DEF(case, "case")
DEF(default, "default")
DEF(throw, "throw")
DEF(try, "try")
DEF(catch, "catch")
DEF(finally, "finally")
DEF(function, "function")
DEF(debugger, "debugger")
DEF(with, "with")
/* FutureReservedWord */
DEF(class, "class")
DEF(const, "const")
DEF(enum, "enum")
DEF(export, "export")
DEF(extends, "extends")
DEF(import, "import")
DEF(super, "super")
/* FutureReservedWords when parsing strict mode code */
DEF(implements, "implements")
DEF(interface, "interface")
DEF(let, "let")
DEF(package, "package")
DEF(private, "private")
DEF(protected, "protected")
DEF(public, "public")
DEF(static, "static")
DEF(yield, "yield")
DEF(await, "await")
/* empty string */
DEF(empty_string, "")
/* identifiers */
DEF(length, "length")
DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber")
DEF(message, "message")
DEF(stack, "stack")
DEF(name, "name")
DEF(toString, "toString")
DEF(toLocaleString, "toLocaleString")
DEF(valueOf, "valueOf")
DEF(eval, "eval")
DEF(prototype, "prototype")
DEF(constructor, "constructor")
DEF(configurable, "configurable")
DEF(writable, "writable")
DEF(enumerable, "enumerable")
DEF(value, "value")
DEF(get, "get")
DEF(set, "set")
DEF(of, "of")
DEF(__proto__, "__proto__")
DEF(undefined, "undefined")
DEF(number, "number")
DEF(boolean, "boolean")
DEF(string, "string")
DEF(object, "object")
DEF(symbol, "symbol")
DEF(integer, "integer")
DEF(unknown, "unknown")
DEF(arguments, "arguments")
DEF(callee, "callee")
DEF(caller, "caller")
DEF(_eval_, "<eval>")
DEF(_ret_, "<ret>")
DEF(_var_, "<var>")
DEF(_with_, "<with>")
DEF(lastIndex, "lastIndex")
DEF(target, "target")
DEF(index, "index")
DEF(input, "input")
DEF(defineProperties, "defineProperties")
DEF(apply, "apply")
DEF(join, "join")
DEF(concat, "concat")
DEF(split, "split")
DEF(construct, "construct")
DEF(getPrototypeOf, "getPrototypeOf")
DEF(setPrototypeOf, "setPrototypeOf")
DEF(isExtensible, "isExtensible")
DEF(preventExtensions, "preventExtensions")
DEF(has, "has")
DEF(deleteProperty, "deleteProperty")
DEF(defineProperty, "defineProperty")
DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor")
DEF(ownKeys, "ownKeys")
DEF(add, "add")
DEF(done, "done")
DEF(next, "next")
DEF(values, "values")
DEF(source, "source")
DEF(flags, "flags")
DEF(global, "global")
DEF(unicode, "unicode")
DEF(raw, "raw")
DEF(new_target, "new.target")
DEF(this_active_func, "this.active_func")
DEF(home_object, "<home_object>")
DEF(as, "as")
DEF(from, "from")
DEF(_default_, "*default*")
DEF(_star_, "*")
DEF(Module, "Module")
DEF(then, "then")
DEF(resolve, "resolve")
DEF(reject, "reject")
DEF(promise, "promise")
DEF(proxy, "proxy")
DEF(revoke, "revoke")
DEF(async, "async")
DEF(exec, "exec")
DEF(groups, "groups")
#ifdef CONFIG_BIGNUM
DEF(bigint, "bigint")
DEF(bigfloat, "bigfloat")
#endif
#ifdef CONFIG_ATOMICS
DEF(not_equal, "not-equal")
DEF(timed_out, "timed-out")
DEF(ok, "ok")
#endif
DEF(toJSON, "toJSON")
/* class names */
DEF(Object, "Object")
DEF(Array, "Array")
DEF(Error, "Error")
DEF(Number, "Number")
DEF(String, "String")
DEF(Boolean, "Boolean")
DEF(Symbol, "Symbol")
DEF(Arguments, "Arguments")
DEF(Math, "Math")
DEF(JSON, "JSON")
DEF(Date, "Date")
DEF(Function, "Function")
DEF(GeneratorFunction, "GeneratorFunction")
DEF(ForInIterator, "ForInIterator")
DEF(RegExp, "RegExp")
DEF(ArrayBuffer, "ArrayBuffer")
DEF(SharedArrayBuffer, "SharedArrayBuffer")
/* must keep same order as class IDs for typed arrays */
DEF(Uint8ClampedArray, "Uint8ClampedArray")
DEF(Int8Array, "Int8Array")
DEF(Uint8Array, "Uint8Array")
DEF(Int16Array, "Int16Array")
DEF(Uint16Array, "Uint16Array")
DEF(Int32Array, "Int32Array")
DEF(Uint32Array, "Uint32Array")
#ifdef CONFIG_BIGNUM
DEF(BigInt64Array, "BigInt64Array")
DEF(BigUint64Array, "BigUint64Array")
#endif
DEF(Float32Array, "Float32Array")
DEF(Float64Array, "Float64Array")
DEF(DataView, "DataView")
#ifdef CONFIG_BIGNUM
DEF(BigInt, "BigInt")
DEF(BigFloat, "BigFloat")
DEF(BigFloatEnv, "BigFloatEnv")
#endif
DEF(Map, "Map")
DEF(Set, "Set") /* Map + 1 */
DEF(WeakMap, "WeakMap") /* Map + 2 */
DEF(WeakSet, "WeakSet") /* Map + 3 */
DEF(Map_Iterator, "Map Iterator")
DEF(Set_Iterator, "Set Iterator")
DEF(Array_Iterator, "Array Iterator")
DEF(String_Iterator, "String Iterator")
DEF(Generator, "Generator")
DEF(Proxy, "Proxy")
DEF(Promise, "Promise")
DEF(PromiseResolveFunction, "PromiseResolveFunction")
DEF(PromiseRejectFunction, "PromiseRejectFunction")
DEF(AsyncFunction, "AsyncFunction")
DEF(AsyncFunctionResolve, "AsyncFunctionResolve")
DEF(AsyncFunctionReject, "AsyncFunctionReject")
DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction")
DEF(AsyncGenerator, "AsyncGenerator")
DEF(EvalError, "EvalError")
DEF(RangeError, "RangeError")
DEF(ReferenceError, "ReferenceError")
DEF(SyntaxError, "SyntaxError")
DEF(TypeError, "TypeError")
DEF(URIError, "URIError")
DEF(InternalError, "InternalError")
/* symbols */
DEF(Symbol_toPrimitive, "Symbol.toPrimitive")
DEF(Symbol_iterator, "Symbol.iterator")
DEF(Symbol_match, "Symbol.match")
DEF(Symbol_replace, "Symbol.replace")
DEF(Symbol_search, "Symbol.search")
DEF(Symbol_split, "Symbol.split")
DEF(Symbol_toStringTag, "Symbol.toStringTag")
DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable")
DEF(Symbol_hasInstance, "Symbol.hasInstance")
DEF(Symbol_species, "Symbol.species")
DEF(Symbol_unscopables, "Symbol.unscopables")
DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
#ifdef CONFIG_BIGNUM
DEF(Symbol_operatorOrder, "Symbol.operatorOrder")
DEF(Symbol_operatorAdd, "Symbol.operatorAdd")
DEF(Symbol_operatorSub, "Symbol.operatorSub")
DEF(Symbol_operatorMul, "Symbol.operatorMul")
DEF(Symbol_operatorDiv, "Symbol.operatorDiv")
DEF(Symbol_operatorMod, "Symbol.operatorMod")
DEF(Symbol_operatorPow, "Symbol.operatorPow")
DEF(Symbol_operatorShl, "Symbol.operatorShl")
DEF(Symbol_operatorShr, "Symbol.operatorShr")
DEF(Symbol_operatorAnd, "Symbol.operatorAnd")
DEF(Symbol_operatorOr, "Symbol.operatorOr")
DEF(Symbol_operatorXor, "Symbol.operatorXor")
DEF(Symbol_operatorCmpLT, "Symbol.operatorCmpLT")
DEF(Symbol_operatorCmpLE, "Symbol.operatorCmpLE")
DEF(Symbol_operatorCmpEQ, "Symbol.operatorCmpEQ")
DEF(Symbol_operatorPlus, "Symbol.operatorPlus")
DEF(Symbol_operatorNeg, "Symbol.operatorNeg")
DEF(Symbol_operatorNot, "Symbol.operatorNot")
DEF(Symbol_operatorInc, "Symbol.operatorInc")
DEF(Symbol_operatorDec, "Symbol.operatorDec")
DEF(Symbol_operatorMathDiv, "Symbol.operatorMathDiv")
DEF(Symbol_operatorMathMod, "Symbol.operatorMathMod")
DEF(Symbol_operatorMathPow, "Symbol.operatorMathPow")
#endif
#endif /* DEF */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
/*
* QuickJS C library
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifndef QUICKJS_LIBC_H
#define QUICKJS_LIBC_H
#include <stdio.h>
#include <stdlib.h>
#include "quickjs.h"
JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
void js_std_loop(JSContext *ctx);
void js_std_free_handlers(JSRuntime *rt);
void js_std_dump_error(JSContext *ctx);
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
JSModuleDef *js_module_loader(JSContext *ctx,
const char *module_name, void *opaque);
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags);
#endif /* QUICKJS_LIBC_H */

View File

@ -0,0 +1,354 @@
/*
* QuickJS opcode definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* 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.
*/
#ifdef FMT
FMT(none)
FMT(none_int)
FMT(none_loc)
FMT(none_arg)
FMT(none_var_ref)
FMT(u8)
FMT(i8)
FMT(loc8)
FMT(const8)
FMT(label8)
FMT(u16)
FMT(i16)
FMT(label16)
FMT(npop)
FMT(npopx)
FMT(loc)
FMT(arg)
FMT(var_ref)
FMT(u32)
FMT(i32)
FMT(const)
FMT(label)
FMT(atom)
FMT(atom_u8)
FMT(atom_u16)
FMT(atom_label_u8)
FMT(atom_label_u16)
FMT(label_u16)
#undef FMT
#endif /* FMT */
#ifdef DEF
#ifndef def
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
#endif
def(invalid, 1, 0, 0, none) /* never emitted */
/* push values */
DEF( push_i32, 5, 0, 1, i32)
DEF( push_const, 5, 0, 1, const)
DEF( fclosure, 5, 0, 1, const) /* must follow push_const */
DEF(push_atom_value, 5, 0, 1, atom)
DEF( undefined, 1, 0, 1, none)
DEF( null, 1, 0, 1, none)
DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
DEF( push_false, 1, 0, 1, none)
DEF( push_true, 1, 0, 1, none)
DEF( object, 1, 0, 1, none)
DEF( var_object, 1, 0, 1, none)
DEF( this_func, 1, 0, 1, none) /* only used at the start of a function */
DEF( arguments, 2, 0, 1, none) /* only used at the start of a function */
DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */
DEF( new_target, 1, 0, 1, none) /* only used at the start of a function */
DEF( home_object, 1, 0, 1, none) /* only used at the start of a function */
DEF( drop, 1, 1, 0, none) /* a -> */
DEF( nip, 1, 2, 1, none) /* a b -> b */
DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
DEF( dup, 1, 1, 2, none) /* a -> a a */
DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
DEF( swap, 1, 2, 2, none) /* a b -> b a */
DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */
DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF( apply, 3, 3, 1, u16)
DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none)
DEF(check_ctor_return, 1, 1, 2, none)
DEF( check_ctor, 1, 0, 0, none)
DEF( return_async, 1, 1, 0, none)
DEF( throw, 1, 1, 0, none)
DEF( throw_var, 6, 0, 0, atom_u8)
DEF( eval, 3, 1, 1, u16)
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
bytecode string */
DEF( get_super_ctor, 1, 1, 1, none)
DEF( get_super, 1, 1, 1, none)
DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */
DEF( get_ref_value, 1, 2, 3, none)
DEF( put_ref_value, 1, 3, 0, none)
DEF( define_var, 6, 0, 0, atom_u8)
DEF(check_define_var, 6, 0, 0, atom_u8)
DEF( define_func, 6, 1, 0, atom_u8)
DEF( get_field, 5, 1, 1, atom)
DEF( get_field2, 5, 1, 2, atom)
DEF( put_field, 5, 2, 0, atom)
DEF( get_array_el, 1, 2, 1, none)
DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
DEF( put_array_el, 1, 3, 0, none)
DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */
DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */
DEF( define_field, 5, 2, 1, atom)
DEF( set_name, 5, 1, 1, atom)
DEF(set_name_computed, 1, 2, 2, none)
DEF( set_proto, 1, 2, 1, none)
DEF(define_array_el, 1, 3, 2, none)
DEF( append, 1, 3, 2, none) /* append enumerated object, update length */
DEF(copy_data_properties, 2, 3, 3, u8)
DEF( define_method, 6, 2, 1, atom_u8)
DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
DEF( define_class, 2, 2, 2, u8)
DEF( get_loc, 3, 0, 1, loc)
DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
DEF( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
DEF( get_var_ref, 3, 0, 1, var_ref)
DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
DEF(set_loc_uninitialized, 3, 0, 0, loc)
def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */
DEF( get_loc_check, 3, 0, 1, loc)
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_var_ref_check, 3, 0, 1, var_ref)
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
DEF( close_loc, 3, 0, 0, loc)
def(close_var_object, 1, 0, 0, none) /* emitted in phase 1, removed in phase 2 */
def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
DEF( if_false, 5, 1, 0, label)
DEF( if_true, 5, 1, 0, label) /* must come after if_false */
DEF( goto, 5, 0, 0, label) /* must come after if_true */
def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
DEF( catch, 5, 0, 1, label)
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
DEF( to_object, 1, 1, 1, none)
//DEF( to_string, 1, 1, 1, none)
DEF( to_propkey, 1, 1, 1, none)
DEF( to_propkey2, 1, 2, 2, none)
DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8)
DEF( make_loc_ref, 7, 0, 2, atom_u16)
DEF( make_arg_ref, 7, 0, 2, atom_u16)
DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
DEF( make_var_ref, 5, 0, 2, atom)
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
DEF( for_in_start, 1, 1, 1, none)
DEF( for_of_start, 1, 1, 3, none)
DEF(for_await_of_start, 1, 1, 3, none)
DEF( for_in_next, 1, 1, 3, none)
DEF( for_of_next, 2, 3, 5, u8)
DEF(for_await_of_next, 1, 3, 4, none)
DEF(iterator_get_value_done, 1, 1, 2, none)
DEF( iterator_close, 1, 3, 0, none)
DEF(iterator_close_return, 1, 4, 4, none)
DEF(async_iterator_close, 1, 3, 2, none)
DEF(async_iterator_next, 1, 4, 4, none)
DEF(async_iterator_get, 2, 4, 5, u8)
DEF( initial_yield, 1, 0, 0, none)
DEF( yield, 1, 1, 2, none)
DEF( yield_star, 1, 2, 2, none)
DEF(async_yield_star, 1, 1, 2, none)
DEF( await, 1, 1, 1, none)
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)
DEF( dec, 1, 1, 1, none)
DEF( inc, 1, 1, 1, none)
DEF( post_dec, 1, 1, 2, none)
DEF( post_inc, 1, 1, 2, none)
DEF( dec_loc, 2, 0, 0, loc8)
DEF( inc_loc, 2, 0, 0, loc8)
DEF( add_loc, 2, 1, 0, loc8)
DEF( not, 1, 1, 1, none)
DEF( lnot, 1, 1, 1, none)
DEF( typeof, 1, 1, 1, none)
DEF( delete, 1, 2, 1, none)
DEF( delete_var, 5, 0, 1, atom)
DEF( mul, 1, 2, 1, none)
DEF( div, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none)
DEF( sar, 1, 2, 1, none)
DEF( shr, 1, 2, 1, none)
DEF( lt, 1, 2, 1, none)
DEF( lte, 1, 2, 1, none)
DEF( gt, 1, 2, 1, none)
DEF( gte, 1, 2, 1, none)
DEF( instanceof, 1, 2, 1, none)
DEF( in, 1, 2, 1, none)
DEF( eq, 1, 2, 1, none)
DEF( neq, 1, 2, 1, none)
DEF( strict_eq, 1, 2, 1, none)
DEF( strict_neq, 1, 2, 1, none)
DEF( and, 1, 2, 1, none)
DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none)
#ifdef CONFIG_BIGNUM
DEF( mul_pow10, 1, 2, 1, none)
DEF( math_div, 1, 2, 1, none)
DEF( math_mod, 1, 2, 1, none)
DEF( math_pow, 1, 2, 1, none)
#endif
#if SHORT_OPCODES
DEF( push_minus1, 1, 0, 1, none_int)
DEF( push_0, 1, 0, 1, none_int)
DEF( push_1, 1, 0, 1, none_int)
DEF( push_2, 1, 0, 1, none_int)
DEF( push_3, 1, 0, 1, none_int)
DEF( push_4, 1, 0, 1, none_int)
DEF( push_5, 1, 0, 1, none_int)
DEF( push_6, 1, 0, 1, none_int)
DEF( push_7, 1, 0, 1, none_int)
DEF( push_i8, 2, 0, 1, i8)
DEF( push_i16, 3, 0, 1, i16)
DEF( push_const8, 2, 0, 1, const8)
DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
DEF(push_empty_string, 1, 0, 1, none)
DEF( get_loc8, 2, 0, 1, loc8)
DEF( put_loc8, 2, 1, 0, loc8)
DEF( set_loc8, 2, 1, 1, loc8)
DEF( get_loc0, 1, 0, 1, none_loc)
DEF( get_loc1, 1, 0, 1, none_loc)
DEF( get_loc2, 1, 0, 1, none_loc)
DEF( get_loc3, 1, 0, 1, none_loc)
DEF( put_loc0, 1, 1, 0, none_loc)
DEF( put_loc1, 1, 1, 0, none_loc)
DEF( put_loc2, 1, 1, 0, none_loc)
DEF( put_loc3, 1, 1, 0, none_loc)
DEF( set_loc0, 1, 1, 1, none_loc)
DEF( set_loc1, 1, 1, 1, none_loc)
DEF( set_loc2, 1, 1, 1, none_loc)
DEF( set_loc3, 1, 1, 1, none_loc)
DEF( get_arg0, 1, 0, 1, none_arg)
DEF( get_arg1, 1, 0, 1, none_arg)
DEF( get_arg2, 1, 0, 1, none_arg)
DEF( get_arg3, 1, 0, 1, none_arg)
DEF( put_arg0, 1, 1, 0, none_arg)
DEF( put_arg1, 1, 1, 0, none_arg)
DEF( put_arg2, 1, 1, 0, none_arg)
DEF( put_arg3, 1, 1, 0, none_arg)
DEF( set_arg0, 1, 1, 1, none_arg)
DEF( set_arg1, 1, 1, 1, none_arg)
DEF( set_arg2, 1, 1, 1, none_arg)
DEF( set_arg3, 1, 1, 1, none_arg)
DEF( get_var_ref0, 1, 0, 1, none_var_ref)
DEF( get_var_ref1, 1, 0, 1, none_var_ref)
DEF( get_var_ref2, 1, 0, 1, none_var_ref)
DEF( get_var_ref3, 1, 0, 1, none_var_ref)
DEF( put_var_ref0, 1, 1, 0, none_var_ref)
DEF( put_var_ref1, 1, 1, 0, none_var_ref)
DEF( put_var_ref2, 1, 1, 0, none_var_ref)
DEF( put_var_ref3, 1, 1, 0, none_var_ref)
DEF( set_var_ref0, 1, 1, 1, none_var_ref)
DEF( set_var_ref1, 1, 1, 1, none_var_ref)
DEF( set_var_ref2, 1, 1, 1, none_var_ref)
DEF( set_var_ref3, 1, 1, 1, none_var_ref)
DEF( get_length, 1, 1, 1, none)
DEF( if_false8, 2, 1, 0, label8)
DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
DEF( goto16, 3, 0, 0, label16)
DEF( call0, 1, 1, 1, npopx)
DEF( call1, 1, 1, 1, npopx)
DEF( call2, 1, 1, 1, npopx)
DEF( call3, 1, 1, 1, npopx)
DEF( is_undefined, 1, 1, 1, none)
DEF( is_null, 1, 1, 1, none)
DEF( is_function, 1, 1, 1, none)
#endif
DEF( nop, 1, 0, 0, none) /* temporary use during code generation */
#undef DEF
#undef def
#endif /* DEF */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,868 @@
/*
* QuickJS Javascript Engine
*
* Copyright (c) 2017-2019 Fabrice Bellard
* Copyright (c) 2017-2019 Charlie Gordon
*
* 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.
*/
#ifndef QUICKJS_H
#define QUICKJS_H
#include <stdio.h>
#include <stdint.h>
#if defined(__GNUC__) || defined(__clang__)
#define js_likely(x) __builtin_expect(!!(x), 1)
#define js_unlikely(x) __builtin_expect(!!(x), 0)
#define js_force_inline inline __attribute__((always_inline))
#define __js_printf_like(f, a) __attribute__((format(printf, f, a)))
#else
#define js_likely(x) (x)
#define js_unlikely(x) (x)
#define js_force_inline inline
#define __js_printf_like(a, b)
#endif
#define JS_BOOL int
typedef struct JSRuntime JSRuntime;
typedef struct JSContext JSContext;
typedef struct JSObject JSObject;
typedef struct JSClass JSClass;
typedef uint32_t JSClassID;
typedef uint32_t JSAtom;
#if defined(__x86_64__) || defined(__aarch64__)
#define JS_PTR64
#define JS_PTR64_DEF(a) a
#else
#define JS_PTR64_DEF(a)
#endif
#ifndef JS_PTR64
#define JS_NAN_BOXING
#endif
enum {
/* all tags with a reference count are negative */
JS_TAG_FIRST = -10, /* first negative tag */
JS_TAG_BIG_INT = -10,
JS_TAG_BIG_FLOAT = -9,
JS_TAG_SYMBOL = -8,
JS_TAG_STRING = -7,
JS_TAG_SHAPE = -6, /* used internally during GC */
JS_TAG_ASYNC_FUNCTION = -5, /* used internally during GC */
JS_TAG_VAR_REF = -4, /* used internally during GC */
JS_TAG_MODULE = -3, /* used internally */
JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */
JS_TAG_OBJECT = -1,
JS_TAG_INT = 0,
JS_TAG_BOOL = 1,
JS_TAG_NULL = 2,
JS_TAG_UNDEFINED = 3,
JS_TAG_UNINITIALIZED = 4,
JS_TAG_CATCH_OFFSET = 5,
JS_TAG_EXCEPTION = 6,
JS_TAG_FLOAT64 = 7,
/* any larger tag is FLOAT64 if JS_NAN_BOXING */
};
typedef struct JSRefCountHeader {
int ref_count;
} JSRefCountHeader;
#define JS_FLOAT64_NAN NAN
#ifdef CONFIG_CHECK_JSVALUE
/* JSValue consistency : it is not possible to run the code in this
mode, but it is useful to detect simple reference counting
errors. It would be interesting to modify a static C analyzer to
handle specific annotations (clang has such annotations but only
for objective C) */
typedef struct __JSValue *JSValue;
typedef const struct __JSValue *JSValueConst;
#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf)
/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)
#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)
#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)
#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))
#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag))
#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1)
static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
{
return JS_MKVAL(JS_TAG_FLOAT64, (int)d);
}
#elif defined(JS_NAN_BOXING)
typedef uint64_t JSValue;
#define JSValueConst JSValue
#define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
#define JS_VALUE_GET_INT(v) (int)(v)
#define JS_VALUE_GET_BOOL(v) (int)(v)
#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr))
#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */
static inline double JS_VALUE_GET_FLOAT64(JSValue v)
{
union {
JSValue v;
double d;
} u;
u.v = v;
u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32;
return u.d;
}
#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32))
static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
{
union {
double d;
uint64_t u64;
} u;
JSValue v;
u.d = d;
/* normalize NaN */
if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000))
v = JS_NAN;
else
v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32);
return v;
}
#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST))
/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
static inline int JS_VALUE_GET_NORM_TAG(JSValue v)
{
uint32_t tag;
tag = JS_VALUE_GET_TAG(v);
if (JS_TAG_IS_FLOAT64(tag))
return JS_TAG_FLOAT64;
else
return tag;
}
#else /* !JS_NAN_BOXING */
typedef union JSValueUnion {
int32_t int32;
double float64;
void *ptr;
} JSValueUnion;
typedef struct JSValue {
JSValueUnion u;
int64_t tag;
} JSValue;
#define JSValueConst JSValue
#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag)
/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)
#define JS_VALUE_GET_INT(v) ((v).u.int32)
#define JS_VALUE_GET_BOOL(v) ((v).u.int32)
#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
#define JS_VALUE_GET_PTR(v) ((v).u.ptr)
#define JS_MKVAL(tag, val) (JSValue){ .u.int32 = val, tag }
#define JS_MKPTR(tag, p) (JSValue){ .u.ptr = p, tag }
#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
{
JSValue v;
v.tag = JS_TAG_FLOAT64;
v.u.float64 = d;
return v;
}
#endif /* !JS_NAN_BOXING */
#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)
#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2)))
#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))
#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))
#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST)
/* special values */
#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0)
#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0)
#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0)
#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1)
#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0)
#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0)
/* flags for object properties */
#define JS_PROP_CONFIGURABLE (1 << 0)
#define JS_PROP_WRITABLE (1 << 1)
#define JS_PROP_ENUMERABLE (1 << 2)
#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)
#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */
#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */
#define JS_PROP_NORMAL (0 << 4)
#define JS_PROP_GETSET (1 << 4)
#define JS_PROP_VARREF (2 << 4) /* used internally */
#define JS_PROP_AUTOINIT (3 << 4) /* used internally */
/* flags for JS_DefineProperty */
#define JS_PROP_HAS_SHIFT 8
#define JS_PROP_HAS_CONFIGURABLE (1 << 8)
#define JS_PROP_HAS_WRITABLE (1 << 9)
#define JS_PROP_HAS_ENUMERABLE (1 << 10)
#define JS_PROP_HAS_GET (1 << 11)
#define JS_PROP_HAS_SET (1 << 12)
#define JS_PROP_HAS_VALUE (1 << 13)
/* throw an exception if false would be returned
(JS_DefineProperty/JS_SetProperty) */
#define JS_PROP_THROW (1 << 14)
/* throw an exception if false would be returned in strict mode
(JS_SetProperty) */
#define JS_PROP_THROW_STRICT (1 << 15)
#define JS_PROP_NO_ADD (1 << 16) /* internal use */
#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */
#define JS_DEFAULT_STACK_SIZE (256 * 1024)
/* JS_Eval() flags */
#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */
#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */
#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */
#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */
#define JS_EVAL_TYPE_MASK (3 << 0)
#define JS_EVAL_FLAG_SHEBANG (1 << 2) /* skip first line beginning with '#!' */
#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */
#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) /* internal use */
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data);
typedef struct JSMallocState {
size_t malloc_count;
size_t malloc_size;
size_t malloc_limit;
void *opaque; /* user opaque */
} JSMallocState;
typedef struct JSMallocFunctions {
void *(*js_malloc)(JSMallocState *s, size_t size);
void (*js_free)(JSMallocState *s, void *ptr);
void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);
size_t (*js_malloc_usable_size)(const void *ptr);
} JSMallocFunctions;
JSRuntime *JS_NewRuntime(void);
/* info lifetime must exceed that of rt */
void JS_SetRuntimeInfo(JSRuntime *rt, const char *info);
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit);
void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold);
JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque);
void JS_FreeRuntime(JSRuntime *rt);
typedef void JS_MarkFunc(JSRuntime *rt, JSValueConst val);
void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
void JS_RunGC(JSRuntime *rt);
JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj);
JS_BOOL JS_IsInGCSweep(JSRuntime *rt);
JSContext *JS_NewContext(JSRuntime *rt);
void JS_FreeContext(JSContext *s);
void *JS_GetContextOpaque(JSContext *ctx);
void JS_SetContextOpaque(JSContext *ctx, void *opaque);
JSRuntime *JS_GetRuntime(JSContext *ctx);
void JS_SetMaxStackSize(JSContext *ctx, size_t stack_size);
void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj);
JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id);
/* the following functions are used to select the intrinsic object to
save memory */
JSContext *JS_NewContextRaw(JSRuntime *rt);
void JS_AddIntrinsicBaseObjects(JSContext *ctx);
void JS_AddIntrinsicDate(JSContext *ctx);
void JS_AddIntrinsicEval(JSContext *ctx);
void JS_AddIntrinsicStringNormalize(JSContext *ctx);
void JS_AddIntrinsicRegExpCompiler(JSContext *ctx);
void JS_AddIntrinsicRegExp(JSContext *ctx);
void JS_AddIntrinsicJSON(JSContext *ctx);
void JS_AddIntrinsicProxy(JSContext *ctx);
void JS_AddIntrinsicMapSet(JSContext *ctx);
void JS_AddIntrinsicTypedArrays(JSContext *ctx);
void JS_AddIntrinsicPromise(JSContext *ctx);
JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv);
void *js_malloc_rt(JSRuntime *rt, size_t size);
void js_free_rt(JSRuntime *rt, void *ptr);
void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size);
size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr);
void *js_mallocz_rt(JSRuntime *rt, size_t size);
void *js_malloc(JSContext *ctx, size_t size);
void js_free(JSContext *ctx, void *ptr);
void *js_realloc(JSContext *ctx, void *ptr, size_t size);
size_t js_malloc_usable_size(JSContext *ctx, const void *ptr);
void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack);
void *js_mallocz(JSContext *ctx, size_t size);
char *js_strdup(JSContext *ctx, const char *str);
char *js_strndup(JSContext *ctx, const char *s, size_t n);
typedef struct JSMemoryUsage {
int64_t malloc_size, malloc_limit, memory_used_size;
int64_t malloc_count;
int64_t memory_used_count;
int64_t atom_count, atom_size;
int64_t str_count, str_size;
int64_t obj_count, obj_size;
int64_t prop_count, prop_size;
int64_t shape_count, shape_size;
int64_t js_func_count, js_func_size, js_func_code_size;
int64_t js_func_pc2line_count, js_func_pc2line_size;
int64_t c_func_count, array_count;
int64_t fast_array_count, fast_array_elements;
int64_t binary_object_count, binary_object_size;
} JSMemoryUsage;
void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s);
void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);
/* atom support */
JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, int len);
JSAtom JS_NewAtom(JSContext *ctx, const char *str);
JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n);
JSAtom JS_DupAtom(JSContext *ctx, JSAtom v);
void JS_FreeAtom(JSContext *ctx, JSAtom v);
void JS_FreeAtomRT(JSRuntime *rt, JSAtom v);
JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom);
JSValue JS_AtomToString(JSContext *ctx, JSAtom atom);
const char *JS_AtomToCString(JSContext *ctx, JSAtom atom);
/* object class support */
typedef struct JSPropertyEnum {
JS_BOOL is_enumerable;
JSAtom atom;
} JSPropertyEnum;
typedef struct JSPropertyDescriptor {
int flags;
JSValue value;
JSValue getter;
JSValue setter;
} JSPropertyDescriptor;
typedef struct JSClassExoticMethods {
/* Return -1 if exception (can only happen in case of Proxy object),
FALSE if the property does not exists, TRUE if it exists. If 1 is
returned, the property descriptor 'desc' is filled if != NULL. */
int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc,
JSValueConst obj, JSAtom prop);
/* '*ptab' should hold the '*plen' property keys. Return 0 if OK,
-1 if exception. The 'is_enumerable' field is ignored.
*/
int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab,
uint32_t *plen,
JSValueConst obj);
/* return < 0 if exception, or TRUE/FALSE */
int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop);
/* return < 0 if exception or TRUE/FALSE */
int (*define_own_property)(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValueConst val,
JSValueConst getter, JSValueConst setter,
int flags);
/* The following methods can be emulated with the previous ones,
so they are usually not needed */
/* return < 0 if exception or TRUE/FALSE */
int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom);
JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
JSValueConst receiver);
/* return < 0 if exception or TRUE/FALSE */
int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
JSValueConst value, JSValueConst receiver, int flags);
} JSClassExoticMethods;
typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);
typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_val, int argc, JSValueConst *argv);
typedef struct JSClassDef {
const char *class_name;
JSClassFinalizer *finalizer;
JSClassGCMark *gc_mark;
JSClassCall *call;
/* XXX: suppress this indirection ? It is here only to save memory
because only a few classes need these methods */
JSClassExoticMethods *exotic;
} JSClassDef;
JSClassID JS_NewClassID(JSClassID *pclass_id);
int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);
int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
/* value handling */
static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)
{
return JS_MKVAL(JS_TAG_BOOL, val);
}
static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val)
{
return JS_MKVAL(JS_TAG_INT, val);
}
static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val)
{
return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);
}
JSValue JS_NewInt64(JSContext *ctx, int64_t v);
static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
{
JSValue v;
int32_t val;
union {
double d;
uint64_t u;
} u, t;
u.d = d;
val = (int32_t)d;
t.d = val;
/* -0 cannot be represented as integer, so we compare the bit
representation */
if (u.u == t.u) {
v = JS_MKVAL(JS_TAG_INT, val);
} else {
v = __JS_NewFloat64(ctx, d);
}
return v;
}
JS_BOOL JS_IsNumber(JSValueConst v);
static inline JS_BOOL JS_IsInteger(JSValueConst v)
{
int tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT;
}
static inline JS_BOOL JS_IsBigFloat(JSValueConst v)
{
int tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG_BIG_FLOAT;
}
static inline JS_BOOL JS_IsBool(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL;
}
static inline JS_BOOL JS_IsNull(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_NULL;
}
static inline JS_BOOL JS_IsUndefined(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED;
}
static inline JS_BOOL JS_IsException(JSValueConst v)
{
return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION);
}
static inline JS_BOOL JS_IsUninitialized(JSValueConst v)
{
return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED);
}
static inline JS_BOOL JS_IsString(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING;
}
static inline JS_BOOL JS_IsSymbol(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL;
}
static inline JS_BOOL JS_IsObject(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT;
}
JSValue JS_Throw(JSContext *ctx, JSValue obj);
JSValue JS_GetException(JSContext *ctx);
JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
void JS_EnableIsErrorProperty(JSContext *ctx, JS_BOOL enable);
void JS_ResetUncatchableError(JSContext *ctx);
JSValue JS_NewError(JSContext *ctx);
JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
void __JS_FreeValue(JSContext *ctx, JSValue v);
static inline void JS_FreeValue(JSContext *ctx, JSValue v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
if (--p->ref_count <= 0) {
__JS_FreeValue(ctx, v);
}
}
}
void __JS_FreeValueRT(JSRuntime *rt, JSValue v);
static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
if (--p->ref_count <= 0) {
__JS_FreeValueRT(rt, v);
}
}
}
static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
p->ref_count++;
}
return (JSValue)v;
}
int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
static int inline JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
{
return JS_ToInt32(ctx, (int32_t*)pres, val);
}
int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val);
int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val);
JSValue JS_NewStringLen(JSContext *ctx, const char *str1, int len1);
JSValue JS_NewString(JSContext *ctx, const char *str);
JSValue JS_NewAtomString(JSContext *ctx, const char *str);
JSValue JS_ToString(JSContext *ctx, JSValueConst val);
JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val);
const char *JS_ToCStringLen(JSContext *ctx, int *plen, JSValueConst val1, JS_BOOL cesu8);
static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1)
{
return JS_ToCStringLen(ctx, NULL, val1, 0);
}
void JS_FreeCString(JSContext *ctx, const char *ptr);
JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id);
JSValue JS_NewObjectClass(JSContext *ctx, int class_id);
JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);
JSValue JS_NewObject(JSContext *ctx);
JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val);
JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val);
JSValue JS_NewArray(JSContext *ctx);
int JS_IsArray(JSContext *ctx, JSValueConst val);
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
JSAtom prop, JSValueConst receiver,
JS_BOOL throw_ref_error);
static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj,
JSAtom prop)
{
return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0);
}
JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
const char *prop);
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
uint32_t idx);
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue val,
int flags);
static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue val)
{
return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW);
}
int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
uint32_t idx, JSValue val);
int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
int64_t idx, JSValue val);
int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
const char *prop, JSValue val);
int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop);
int JS_IsExtensible(JSContext *ctx, JSValueConst obj);
int JS_PreventExtensions(JSContext *ctx, JSValueConst obj);
int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags);
int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val);
JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val);
JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
const char *filename);
JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
int argc, JSValueConst *argv);
JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
int argc, JSValueConst *argv);
JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
int argc, JSValueConst *argv);
JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
JSValueConst new_target,
int argc, JSValueConst *argv);
JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
const char *filename, int eval_flags);
#define JS_EVAL_BINARY_LOAD_ONLY (1 << 0) /* only load the module */
JSValue JS_EvalBinary(JSContext *ctx,
const uint8_t *buf, size_t buf_len, int flags);
JSValue JS_GetGlobalObject(JSContext *ctx);
int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj);
int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValueConst val,
JSValueConst getter, JSValueConst setter, int flags);
int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue val, int flags);
int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
uint32_t idx, JSValue val, int flags);
int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
const char *prop, JSValue val, int flags);
int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue getter, JSValue setter,
int flags);
void JS_SetOpaque(JSValue obj, void *opaque);
void *JS_GetOpaque(JSValueConst obj, JSClassID class_id);
void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id);
typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr);
JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
JSFreeArrayBufferDataFunc *free_func, void *opaque,
JS_BOOL is_shared);
JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len);
void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj);
uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj);
/* return != 0 if the JS code needs to be interrupted */
typedef int JSInterruptHandler(JSRuntime *rt, void *opaque);
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
/* if can_block is TRUE, Atomics.wait() can be used */
void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
typedef struct JSModuleDef JSModuleDef;
/* return the module specifier (allocated with js_malloc()) or NULL if
exception */
typedef char *JSModuleNormalizeFunc(JSContext *ctx,
const char *module_base_name,
const char *module_name, void *opaque);
typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx,
const char *module_name, void *opaque);
/* module_normalize = NULL is allowed and invokes the default module
filename normalizer */
void JS_SetModuleLoaderFunc(JSRuntime *rt,
JSModuleNormalizeFunc *module_normalize,
JSModuleLoaderFunc *module_loader, void *opaque);
/* JS Job support */
typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv);
int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv);
JS_BOOL JS_IsJobPending(JSRuntime *rt);
int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx);
/* Object Writer/Reader (currently only used to handle precompiled code) */
#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */
uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
int flags);
#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */
JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags);
JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj, JSValueConst this_obj);
/* C function definition */
typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
JS_CFUNC_generic,
JS_CFUNC_generic_magic,
JS_CFUNC_constructor,
JS_CFUNC_constructor_magic,
JS_CFUNC_constructor_or_func,
JS_CFUNC_constructor_or_func_magic,
JS_CFUNC_f_f,
JS_CFUNC_f_f_f,
JS_CFUNC_getter,
JS_CFUNC_setter,
JS_CFUNC_getter_magic,
JS_CFUNC_setter_magic,
JS_CFUNC_iterator_next,
} JSCFunctionEnum;
typedef union JSCFunctionType {
JSCFunction *generic;
JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
JSCFunction *constructor;
JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic);
JSCFunction *constructor_or_func;
double (*f_f)(double);
double (*f_f_f)(double, double);
JSValue (*getter)(JSContext *ctx, JSValueConst this_val);
JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val);
JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic);
JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic);
JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int *pdone, int magic);
} JSCFunctionType;
JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
const char *name,
int length, JSCFunctionEnum cproto, int magic);
JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
int length, int magic, int data_len,
JSValueConst *data);
static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
int length)
{
return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0);
}
static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
const char *name,
int length, JSCFunctionEnum cproto, int magic)
{
return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic);
}
/* C property definition */
typedef struct JSCFunctionListEntry {
const char *name;
uint8_t prop_flags;
uint8_t def_type;
int16_t magic;
union {
struct {
uint8_t length; /* XXX: should move outside union */
uint8_t cproto; /* XXX: should move outside union */
JSCFunctionType cfunc;
} func;
struct {
JSCFunctionType get;
JSCFunctionType set;
} getset;
struct {
const char *name;
int base;
} alias;
struct {
const struct JSCFunctionListEntry *tab;
int len;
} prop_list;
const char *str;
int32_t i32;
int64_t i64;
double f64;
} u;
} JSCFunctionListEntry;
#define JS_DEF_CFUNC 0
#define JS_DEF_CGETSET 1
#define JS_DEF_CGETSET_MAGIC 2
#define JS_DEF_PROP_STRING 3
#define JS_DEF_PROP_INT32 4
#define JS_DEF_PROP_INT64 5
#define JS_DEF_PROP_DOUBLE 6
#define JS_DEF_PROP_UNDEFINED 7
#define JS_DEF_OBJECT 8
#define JS_DEF_ALIAS 9
#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u.func = { length, JS_CFUNC_generic, { .generic = func1 } } }
#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u.func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } }
#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u.func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } }
#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u.func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } }
#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u.getset.get.getter = fgetter, .u.getset.set.setter = fsetter }
#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u.getset.get.getter_magic = fgetter, .u.getset.set.setter_magic = fsetter }
#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u.str = cstr }
#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u.i32 = val }
#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u.i64 = val }
#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u.f64 = val }
#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u.i32 = 0 }
#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u.prop_list = { tab, len } }
#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u.alias = { from, -1 } }
#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u.alias = { from, base } }
void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
const JSCFunctionListEntry *tab,
int len);
/* C module definition */
typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m);
JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
JSModuleInitFunc *func);
/* can only be called before the module is instantiated */
int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str);
int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
const JSCFunctionListEntry *tab, int len);
/* can only be called after the module is instantiated */
int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
JSValue val);
int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
const JSCFunctionListEntry *tab, int len);
#undef js_unlikely
#undef js_force_inline
#endif /* QUICKJS_H */

View File

@ -0,0 +1 @@
The main documentation is in doc/quickjs.pdf or doc/quickjs.html.

View File

@ -0,0 +1,85 @@
#!/bin/sh
# Release the QuickJS source code
set -e
version=`cat VERSION`
if [ "$1" = "-h" ] ; then
echo "release.sh [all]"
echo ""
echo "all: build all the archives. Otherwise only build the quickjs source archive."
exit 1
fi
if [ "$1" = "all" ] ; then
#################################################"
# unicode data
d="quickjs-${version}"
name="quickjs-unicode-data-${version}"
outdir="/tmp/${d}"
rm -rf $outdir
mkdir -p $outdir $outdir/unicode
cp unicode/* $outdir/unicode
( cd /tmp && tar Jcvf /tmp/${name}.tar.xz ${d} )
#################################################"
# all tests
d="quickjs-${version}"
name="quickjs-tests-${version}"
outdir="/tmp/${d}"
rm -rf $outdir
mkdir -p $outdir $outdir/test262o $outdir/test262 $outdir/tests
cp -a test262o/test $outdir/test262o
cp -a test262/test test262/harness $outdir/test262
cp -a tests/bench-v8 $outdir/tests
( cd /tmp && tar Jcvf /tmp/${name}.tar.xz ${d} )
fi # all
#################################################"
# quickjs
make build_doc
d="quickjs-${version}"
outdir="/tmp/${d}"
rm -rf $outdir
mkdir -p $outdir $outdir/doc $outdir/tests $outdir/examples
cp Makefile VERSION TODO readme.txt release.sh \
qjs.c qjsc.c qjscalc.js repl.js \
quickjs.c quickjs.h quickjs-atom.h \
quickjs-libc.c quickjs-libc.h quickjs-opcode.h \
cutils.c cutils.h list.h \
libregexp.c libregexp.h libregexp-opcode.h \
libunicode.c libunicode.h libunicode-table.h \
libbf.c libbf.h \
jscompress.c unicode_gen.c unicode_gen_def.h \
bjson.c \
run-test262.c test262o.conf test262.conf test262bn.conf \
test262o_errors.txt test262_errors.txt test262bn_errors.txt \
$outdir
cp tests/*.js tests/*.patch $outdir/tests
cp examples/*.js examples/*.c $outdir/examples
cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \
doc/jsbignum.texi doc/jsbignum.html doc/jsbignum.pdf \
$outdir/doc
( cd /tmp && tar Jcvf /tmp/${d}.tar.xz ${d} )

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,173 @@
[config]
# general settings for test262 ES6 version
# framework style: old, new
style=new
# handle tests tagged as [noStrict]: yes, no, skip
nostrict=yes
# handle tests tagged as [strictOnly]: yes, no, skip
strict=yes
# test mode: default, default-nostrict, default-strict, strict, nostrict, both, all
mode=default
# handle tests flagged as [async]: yes, no, skip
# for these, load 'harness/doneprintHandle.js' prior to test
# and expect `print('Test262:AsyncTestComplete')` to be called for
# successful termination
async=yes
# handle tests flagged as [module]: yes, no, skip
module=yes
# output error messages: yes, no
verbose=yes
# load harness files from this directory
harnessdir=test262/harness
# names of harness include files to skip
#harnessexclude=
# name of the error file for known errors
errorfile=test262_errors.txt
# exclude tests enumerated in this file (see also [exclude] section)
#excludefile=test262_exclude.txt
# report test results to this file
reportfile=test262_report.txt
# enumerate tests from this directory
testdir=test262/test
[features]
# Standard language features and proposed extensions
# list the features that are included
# skipped features are tagged as such to avoid warnings
Array.prototype.flat
Array.prototype.flatMap
Array.prototype.flatten
Array.prototype.values
ArrayBuffer
arrow-function
async-functions
async-iteration
Atomics
BigInt=skip
caller
class
class-fields-private=skip
class-fields-public=skip
computed-property-names
const
cross-realm=skip
DataView
DataView.prototype.getFloat32
DataView.prototype.getFloat64
DataView.prototype.getInt16
DataView.prototype.getInt32
DataView.prototype.getInt8
DataView.prototype.getUint16
DataView.prototype.getUint32
DataView.prototype.setUint8
default-arg
default-parameters
destructuring-assignment
destructuring-binding
export-star-as-namespace-from-module=skip
Float32Array
Float64Array
for-of
generators
global
Int8Array
IsHTMLDDA=skip
json-superset
let
Map
new.target
numeric-separator-literal
object-rest
object-spread
Object.is
optional-catch-binding
Promise.prototype.finally
Proxy
Reflect
Reflect.construct
Reflect.set
Reflect.setPrototypeOf
regexp-dotall
regexp-lookbehind
regexp-named-groups
regexp-unicode-property-escapes
Set
SharedArrayBuffer
string-trimming
String.fromCodePoint
String.prototype.endsWith
String.prototype.includes
String.prototype.matchAll=skip
String.prototype.trimEnd
String.prototype.trimStart
super
Symbol
Symbol.asyncIterator
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.iterator
Symbol.match
Symbol.matchAll=skip
Symbol.prototype.description
Symbol.replace
Symbol.search
Symbol.species
Symbol.split
Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables
tail-call-optimization=skip
template
TypedArray
u180e
Uint16Array
Uint8Array
Uint8ClampedArray
WeakMap
WeakSet
[exclude]
# list excluded tests and directories here
# intl not supported
test262/test/intl402/
# these builtins are not supported:
test262/test/built-ins/BigInt/
# mislabelled feature Symbol.match -> Symbol.matchAll
test262/test/built-ins/Symbol/matchAll/prop-desc.js
# incompatible with the "caller" feature
test262/test/built-ins/Function/prototype/restricted-property-caller.js
test262/test/built-ins/ThrowTypeError/unique-per-realm-function-proto.js
# no debugger keyword support
test262/test/language/statements/debugger/statement.js
# bogus html close comment test with syntax error
test262/test/annexB/built-ins/Function/createdynfn-html-close-comment-params.js
# bogus test #14 compares 2 consecutive calls to Date(), may be different if unlucky
#test262/test/built-ins/Date/S15.9.2.1_A2.js
# slow tests
#test262/test/built-ins/RegExp/CharacterClassEscapes/
#test262/test/built-ins/RegExp/property-escapes/
[tests]
# list test files or use config.testdir

View File

@ -0,0 +1,175 @@
[config]
# general settings for test262 ES6 bignum version
# framework style: old, new
style=new
# handle tests tagged as [noStrict]: yes, no, skip
nostrict=yes
# handle tests tagged as [strictOnly]: yes, no, skip
strict=yes
# test mode: default, default-nostrict, default-strict, strict, nostrict, both, all
mode=default
# handle tests flagged as [async]: yes, no, skip
# for these, load 'harness/doneprintHandle.js' prior to test
# and expect `print('Test262:AsyncTestComplete')` to be called for
# successful termination
async=yes
# handle tests flagged as [module]: yes, no, skip
module=yes
# output error messages: yes, no
verbose=yes
# load harness files from this directory
harnessdir=test262/harness
# names of harness include files to skip
# bignum version does not support Atomics
harnessexclude=testAtomics.js
# name of the error file for known errors
errorfile=test262bn_errors.txt
# exclude tests enumerated in this file (see also [exclude] section)
#excludefile=test262bn_exclude.txt
# report test results to this file
reportfile=test262bn_report.txt
# enumerate tests from this directory
testdir=test262/test
[features]
# Standard language features and proposed extensions
# list the features that are included
# skipped features are tagged as such to avoid warnings
Array.prototype.flat
Array.prototype.flatMap
Array.prototype.flatten
Array.prototype.values
ArrayBuffer
arrow-function
async-functions
async-iteration
Atomics=skip
BigInt
caller
class
class-fields-private=skip
class-fields-public=skip
computed-property-names
const
cross-realm=skip
DataView
DataView.prototype.getFloat32
DataView.prototype.getFloat64
DataView.prototype.getInt16
DataView.prototype.getInt32
DataView.prototype.getInt8
DataView.prototype.getUint16
DataView.prototype.getUint32
DataView.prototype.setUint8
default-arg
default-parameters
destructuring-assignment
destructuring-binding
export-star-as-namespace-from-module=skip
Float32Array
Float64Array
for-of
generators
global
Int8Array
IsHTMLDDA=skip
json-superset
let
Map
new.target
numeric-separator-literal
object-rest
object-spread
Object.is
optional-catch-binding
Promise.prototype.finally
Proxy
Reflect
Reflect.construct
Reflect.set
Reflect.setPrototypeOf
regexp-dotall
regexp-lookbehind
regexp-named-groups
regexp-unicode-property-escapes
Set
SharedArrayBuffer=skip
string-trimming
String.fromCodePoint
String.prototype.endsWith
String.prototype.includes
String.prototype.matchAll=skip
String.prototype.trimEnd
String.prototype.trimStart
super
Symbol
Symbol.asyncIterator
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.iterator
Symbol.match
Symbol.matchAll=skip
Symbol.prototype.description
Symbol.replace
Symbol.search
Symbol.species
Symbol.split
Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables
tail-call-optimization=skip
template
TypedArray
u180e
Uint16Array
Uint8Array
Uint8ClampedArray
WeakMap
WeakSet
[exclude]
# list excluded tests and directories here
# intl not supported
test262/test/intl402/
# these builtins are not supported:
test262/test/built-ins/Atomics/
test262/test/built-ins/SharedArrayBuffer/
# mislabelled feature Symbol.match -> Symbol.matchAll
test262/test/built-ins/Symbol/matchAll/prop-desc.js
# incompatible with the "caller" feature
test262/test/built-ins/Function/prototype/restricted-property-caller.js
test262/test/built-ins/ThrowTypeError/unique-per-realm-function-proto.js
# no debugger keyword support
test262/test/language/statements/debugger/statement.js
# bogus html close comment test with syntax error
test262/test/annexB/built-ins/Function/createdynfn-html-close-comment-params.js
# bogus test #14 compares 2 consecutive calls to Date(), may be different if unlucky
#test262/test/built-ins/Date/S15.9.2.1_A2.js
# slow tests
#test262/test/built-ins/RegExp/CharacterClassEscapes/
#test262/test/built-ins/RegExp/property-escapes/
[tests]
# list test files or use config.testdir

View File

@ -0,0 +1,404 @@
[config]
# general settings for test262 ES5 version
# framework style: old, new
style=old
# handle tests tagged as @noStrict: yes, no, skip
nostrict=yes
# handle tests tagged as @strictOnly: yes, no, skip
strict=yes
# test mode: default, default-nostrict, default-strict, strict, nostrict, both, all
mode=default
# output error messages: yes, no
verbose=yes
# load harness files this directory
harnessdir=test262o/test/harness
# name of the error file for known errors
errorfile=test262o_errors.txt
# exclude tests enumerated in this file
#excludefile=test262o_excluded.txt
# report test results to this file
reportfile=test262o_report.txt
# enumerate tests from this directory
testdir=test262o/test/suite
[exclude]
# list excluded tests and directories here
# intl not supported
test262o/test/suite/intl402/
# ES6 != ES5: block scoped function definitions allowed in strict mode
test262o/test/suite/bestPractice/Sbp_A1_T1.js
test262o/test/suite/bestPractice/Sbp_A2_T1.js
test262o/test/suite/bestPractice/Sbp_A2_T2.js
test262o/test/suite/bestPractice/Sbp_A3_T1.js
test262o/test/suite/bestPractice/Sbp_A3_T2.js
test262o/test/suite/bestPractice/Sbp_A4_T1.js
test262o/test/suite/bestPractice/Sbp_A4_T2.js
test262o/test/suite/bestPractice/Sbp_A5_T2.js
# ES6 != ES5: `y={x};` is shorthand for `y={x:x}`
test262o/test/suite/ch12/12.1/S12.1_A4_T2.js
test262o/test/suite/ch12/12.6/12.6.4/S12.6.4_A15.js
# ES6 != ES5: function length property is configurable
test262o/test/suite/ch11/11.4/11.4.1/11.4.1-5-a-28-s.js
test262o/test/suite/ch13/13.2/13.2-15-1.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.1/S15.1.2.1_A4.2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A9.2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A7.2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.4/S15.1.2.4_A2.2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.5/S15.1.2.5_A2.2.js
test262o/test/suite/ch15/15.1/15.1.3/15.1.3.1/S15.1.3.1_A5.2.js
test262o/test/suite/ch15/15.1/15.1.3/15.1.3.2/S15.1.3.2_A5.2.js
test262o/test/suite/ch15/15.1/15.1.3/15.1.3.3/S15.1.3.3_A5.2.js
test262o/test/suite/ch15/15.1/15.1.3/15.1.3.4/S15.1.3.4_A5.2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-186.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-187.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-191.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-194.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-201.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.2/S15.2.4.2_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.3/S15.2.4.3_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.4/S15.2.4.4_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.5/S15.2.4.5_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.6/S15.2.4.6_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.7/S15.2.4.7_A9.js
test262o/test/suite/ch15/15.3/15.3.3/15.3.3.2/15.3.3.2-1.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.2/S15.3.4.2_A9.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.3/S15.3.4.3_A9.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.4/S15.3.4.4_A9.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-15-2.js
test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T1.js
test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T2.js
test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T3.js
test262o/test/suite/ch15/15.4/15.4.3/S15.4.3_A2.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.2/S15.4.4.2_A4.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.3/S15.4.4.3_A4.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.4/S15.4.4.4_A4.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.5/S15.4.4.5_A6.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A6.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.8/S15.4.4.8_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.9/S15.4.4.9_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.11/S15.4.4.11_A7.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.13/S15.4.4.13_A5.2.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.4/S15.5.4.4_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.5/S15.5.4.5_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.6/S15.5.4.6_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.7/S15.5.4.7_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.8/S15.5.4.8_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.9/S15.5.4.9_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.10/S15.5.4.10_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.11/S15.5.4.11_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.12/S15.5.4.12_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.13/S15.5.4.13_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.14/S15.5.4.14_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.15/S15.5.4.15_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.16/S15.5.4.16_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.17/S15.5.4.17_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.18/S15.5.4.18_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.19/S15.5.4.19_A9.js
test262o/test/suite/ch15/15.9/15.9.4/15.9.4.2/S15.9.4.2_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.4/15.9.4.3/S15.9.4.3_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.1/S15.9.5.1_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.2/S15.9.5.2_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.3/S15.9.5.3_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.4/S15.9.5.4_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.5/S15.9.5.5_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.6/S15.9.5.6_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.7/S15.9.5.7_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.8/S15.9.5.8_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.9/S15.9.5.9_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.10/S15.9.5.10_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.11/S15.9.5.11_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.12/S15.9.5.12_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.13/S15.9.5.13_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.14/S15.9.5.14_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.15/S15.9.5.15_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.16/S15.9.5.16_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.17/S15.9.5.17_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.18/S15.9.5.18_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.19/S15.9.5.19_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.20/S15.9.5.20_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.21/S15.9.5.21_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.22/S15.9.5.22_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.23/S15.9.5.23_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.24/S15.9.5.24_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.25/S15.9.5.25_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.26/S15.9.5.26_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.27/S15.9.5.27_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.28/S15.9.5.28_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.29/S15.9.5.29_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.30/S15.9.5.30_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.31/S15.9.5.31_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.32/S15.9.5.32_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.33/S15.9.5.33_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.34/S15.9.5.34_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.35/S15.9.5.35_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.36/S15.9.5.36_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.37/S15.9.5.37_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.38/S15.9.5.38_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.39/S15.9.5.39_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.40/S15.9.5.40_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.41/S15.9.5.41_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.42/S15.9.5.42_A3_T2.js
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.2/S15.10.6.2_A9.js
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.3/S15.10.6.3_A9.js
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.4/S15.10.6.4_A9.js
# ES6 != ES5: object literals may have duplicates
test262o/test/suite/ch11/11.1/11.1.5/11.1.5-4-4-a-1-s.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-b-1.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-b-2.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-c-1.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-c-2.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-1.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-2.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-3.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-4.js
# ES6 != ES5: Date.prototype is no longer an instance of Date
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.40/15.9.5.40_1.js
# ES6 != ES5: Object.getPrototypeOf converts argument to object
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1.js
# ES6 != ES5: Object.getPrototypeOf(NativeError)
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-12.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-13.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-14.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-15.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-16.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-17.js
# ES6 != ES5: Object.getOwnPropertyDescriptor converts argument to object
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1.js
# ES6 != ES5: Object.getOwnPropertyNames converts argument to object
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1-5.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1.js
# ES6 != ES5: Object.seal accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1.js
# ES6 != ES5: Object.freeze accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1.js
# ES6 != ES5: Object.preventExtensions accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1.js
# ES6 != ES5: Object.isSealed accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.11/15.2.3.11-1.js
# ES6 != ES5: Object.isFrozen accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1.js
# ES6 != ES5: Object.isExtensible accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1.js
# ES6 != ES5: Object.keys converts argument to object
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-3.js
# ES6 != ES5: source and other properties of RegExp.prototype are not own properties
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-212.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-213.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-214.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-215.js
# ES6 != ES5: String numeric object properties are enumerated first
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-4-44.js
# ES6: new RegExp(regex, flags) is valid
test262o/test/suite/ch15/15.10/15.10.3/S15.10.3.1_A2_T1.js
test262o/test/suite/ch15/15.10/15.10.3/S15.10.3.1_A2_T2.js
test262o/test/suite/ch15/15.10/15.10.4/15.10.4.1/15.10.4.1-1.js
test262o/test/suite/ch15/15.10/15.10.4/S15.10.4.1_A2_T1.js
test262o/test/suite/ch15/15.10/15.10.4/S15.10.4.1_A2_T2.js
# ES6 != ES5: RegExp.prototype.test behavior
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.2/S15.10.6.2_A5_T3.js
# ES6 != ES5: source, global, ignoreCase, multiline, lastIndex are not data properties
# of RegExp objects and RegExp.prototype is not a RegExp object
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/15.10.7.1-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/15.10.7.1-2.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A8.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A9.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A10.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/15.10.7.2-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/15.10.7.2-2.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A8.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A9.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A10.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/15.10.7.3-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/15.10.7.3-2.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A8.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A9.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A10.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/15.10.7.4-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/15.10.7.4-2.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A8.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A9.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A10.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.5/15.10.7.5-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.5/15.10.7.5-2.js
# ES6 != ES5: Error.prototype is a normal object
test262o/test/suite/ch15/15.11/15.11.4/S15.11.4_A2.js
# ES6 different ToLength() semantics
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.5/S15.4.4.5_A4_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A2_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T1.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A2_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A4_T1.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A4_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.8/S15.4.4.8_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.9/S15.4.4.9_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T1.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.11/S15.4.4.11_A4_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A3_T1.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.13/S15.4.4.13_A3_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-8.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-28.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-29.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-28.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-8.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-29.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-8.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-28.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-29.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-8.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-28.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-29.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-25.js
# ES6 different ToLength() semantics causes near infinite runtime
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-14.js
# ES6 arguments/caller changes
test262o/test/suite/ch10/10.6/10.6-13-b-1-s.js
test262o/test/suite/ch10/10.6/10.6-13-b-2-s.js
test262o/test/suite/ch10/10.6/10.6-13-b-3-s.js
test262o/test/suite/ch10/10.6/10.6-14-1-s.js
test262o/test/suite/ch10/10.6/10.6-14-b-1-s.js
test262o/test/suite/ch10/10.6/10.6-14-b-4-s.js
test262o/test/suite/ch13/13.2/13.2-29-s.js
test262o/test/suite/ch13/13.2/13.2-30-s.js
test262o/test/suite/ch13/13.2/13.2-31-s.js
test262o/test/suite/ch13/13.2/13.2-32-s.js
test262o/test/suite/ch13/13.2/13.2-33-s.js
test262o/test/suite/ch13/13.2/13.2-34-s.js
test262o/test/suite/ch13/13.2/13.2-35-s.js
test262o/test/suite/ch13/13.2/13.2-36-s.js
test262o/test/suite/ch13/13.2/S13.2.3_A1.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-1.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-4.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-5.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-1.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-4.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-5.js
# u180e is no longer considered as a space
test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A2.js
test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A3_T1.js
test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A3_T2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A2_T10.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A2_T10.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-2.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-3.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-4.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-5.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-6.js
test262o/test/suite/ch15/15.10/15.10.2/15.10.2.12/S15.10.2.12_A1_T1.js
test262o/test/suite/ch15/15.10/15.10.2/15.10.2.12/S15.10.2.12_A2_T1.js
# E6 eval return value is different
test262o/test/suite/ch12/12.6/12.6.3/S12.6.3_A9.js
test262o/test/suite/ch12/12.6/12.6.3/S12.6.3_A9.1.js
# ECMA 2019 optional-catch-binding feature allows try{}catch{}
test262o/test/suite/ch12/12.14/S12.14_A16_T4.js
[tests]
# list test files or use config.testdir

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,683 @@
diff --git a/harness/doneprintHandle.js b/harness/doneprintHandle.js
index 2b4ab88..6c95f9f 100644
--- a/harness/doneprintHandle.js
+++ b/harness/doneprintHandle.js
@@ -11,7 +11,7 @@ function __consolePrintHandle__(msg){
function $DONE(){
if(!arguments[0])
- __consolePrintHandle__('Test262:AsyncTestComplete');
+ $async_done = true;
else
- __consolePrintHandle__('Test262:AsyncTestFailure:' + arguments[0]);
+ __consolePrintHandle__('Error:' + arguments[0]);
}
diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js
index 362a6d16..d3dde03 100644
--- a/harness/regExpUtils.js
+++ b/harness/regExpUtils.js
@@ -5,6 +5,7 @@ description: |
Collection of functions used to assert the correctness of RegExp objects.
---*/
+/*
function buildString({ loneCodePoints, ranges }) {
const CHUNK_SIZE = 10000;
let result = String.fromCodePoint(...loneCodePoints);
@@ -21,6 +22,32 @@ function buildString({ loneCodePoints, ranges }) {
}
return result;
}
+*/
+
+var codePointRange;
+
+if ($262 && typeof $262.codePointRange === "function") {
+ /* use C function to build the codePointRange (much faster with
+ slow JS engines) */
+ codePointRange = $262.codePointRange;
+} else {
+ codePointRange = function codePointRange(start, end) {
+ const codePoints = [];
+ let length = 0;
+ for (codePoint = start; codePoint < end; codePoint++) {
+ codePoints[length++] = codePoint;
+ }
+ return String.fromCodePoint.apply(null, codePoints);
+ }
+}
+
+function buildString({ loneCodePoints, ranges }) {
+ let result = String.fromCodePoint.apply(null, loneCodePoints);
+ for (const [start, end] of ranges) {
+ result += codePointRange(start, end + 1);
+ }
+ return result;
+}
function testPropertyEscapes(regex, string, expression) {
if (!regex.test(string)) {
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-flags-u.js
index 652ef43..8d1a7ed 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\d/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-plus-quantifier-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-plus-quantifier-flags-u.js
index 8683600..36d9f1c 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-plus-quantifier-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-plus-quantifier-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\d+/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-plus-quantifier.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-plus-quantifier.js
index 20d6b38..e9c91f8 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-plus-quantifier.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-plus-quantifier.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\d+/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape.js
index d3aef45..b2c949b 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\d/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-flags-u.js
index ead3d68..beaf9cf 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\D/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier-flags-u.js
index 5c02690..0030b23 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\D+/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier.js
index 960d9cf..b663be0 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\D+/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape.js
index 85b1dcc..dcbd2eb 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\D/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-flags-u.js
index eda913b..cb57074 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\S/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier-flags-u.js
index 8e21a55..026309c 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\S+/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier.js
index f7cc4a3..98cfdff 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\S+/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape.js
index 3bca8ae..4ec88be 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\S/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-flags-u.js
index 3da454a..0a94a16 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\W/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier-flags-u.js
index 7e7faad..0a10667 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\W+/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier.js
index 41ffa53..55447f8 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\W+/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape.js
index ca47608..c1580cb 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\W/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-flags-u.js
index 82bb950..ac17d3b 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\s/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-plus-quantifier-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-plus-quantifier-flags-u.js
index 7a3b57d..f74a350 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-plus-quantifier-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-plus-quantifier-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\s+/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-plus-quantifier.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-plus-quantifier.js
index e1084ff..ba93158 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-plus-quantifier.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-plus-quantifier.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\s+/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape.js
index d1a3fda..0de155f 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\s/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-flags-u.js
index 0b8b183..2ccf8bf 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\w/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-plus-quantifier-flags-u.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-plus-quantifier-flags-u.js
index d824426..b389118 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-plus-quantifier-flags-u.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-plus-quantifier-flags-u.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0x10ffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0x10FFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0x10ffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\w+/ug;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-plus-quantifier.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-plus-quantifier.js
index bafffd7..334290d 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-plus-quantifier.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-plus-quantifier.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\w+/g;
diff --git a/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape.js b/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape.js
index cba30b0..afc5615 100644
--- a/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape.js
+++ b/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape.js
@@ -33,14 +33,16 @@ info: |
The production CharacterClassEscape :: W evaluates as follows:
Return the set of all characters not included in the set returned by CharacterClassEscape :: w.
features: [String.fromCodePoint]
+includes: [regExpUtils.js]
---*/
const chunks = [];
-const totalChunks = Math.ceil(0xffff / 0x10000);
-
-for (let codePoint = 0; codePoint < 0xFFFF; codePoint++) {
- // split strings to avoid a super long one;
- chunks[codePoint % totalChunks] += String.fromCodePoint(codePoint);
+const lastCodePoint = 0xffff;
+let codePoint = 0;
+while (codePoint <= lastCodePoint) {
+ let n = Math.min(0x10000, lastCodePoint - codePoint + 1);
+ chunks.push(codePointRange(codePoint, codePoint + n));
+ codePoint += n;
}
const re = /\w/g;

View File

@ -0,0 +1,269 @@
"use math";
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function pow(a, n)
{
var r, i;
r = 1;
for(i = 0; i < n; i++)
r *= a;
return r;
}
function test_integer()
{
var a, r;
a = pow(3, 100);
assert((a - 1) != a);
assert(a == 515377520732011331036461129765621272702107522001);
assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1);
assert(Integer.isInteger(1) === true);
assert(Integer.isInteger(1.0) === false);
assert(Integer.floorLog2(0) === -1);
assert(Integer.floorLog2(7) === 2);
r = 1 << 31;
assert(r, 2147483648, "1 << 31 === 2147483648");
r = 1 << 32;
assert(r, 4294967296, "1 << 32 === 4294967296");
r = (1 << 31) < 0;
assert(r, false, "(1 << 31) < 0 === false");
}
function test_divrem(div1, a, b, q)
{
var div, divrem, t;
div = Integer[div1];
divrem = Integer[div1 + "rem"];
assert(div(a, b) == q);
t = divrem(a, b);
assert(t[0] == q);
assert(a == b * q + t[1]);
}
function test_idiv1(div, a, b, r)
{
test_divrem(div, a, b, r[0]);
test_divrem(div, -a, b, r[1]);
test_divrem(div, a, -b, r[2]);
test_divrem(div, -a, -b, r[3]);
}
function test_idiv()
{
test_idiv1("tdiv", 3, 2, [1, -1, -1, 1]);
test_idiv1("fdiv", 3, 2, [1, -2, -2, 1]);
test_idiv1("cdiv", 3, 2, [2, -1, -1, 2]);
test_idiv1("ediv", 3, 2, [1, -2, -1, 2]);
}
function test_float()
{
var e, a, b, sqrt2;
assert(typeof 1 === "bigint");
assert(typeof 1.0 === "bigfloat");
assert(1 == 1.0);
assert(1 !== 1.0);
e = new BigFloatEnv(128);
assert(e.prec == 128);
a = BigFloat.sqrt(2, e);
assert(a == BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e));
assert(e.inexact === true);
assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcd);
b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128);
assert(a == b);
}
/* jscalc tests */
function test_modulo()
{
var i, p, a, b;
/* Euclidian modulo operator */
assert((-3) % 2 == 1);
assert(3 % (-2) == 1);
p = 101;
for(i = 1; i < p; i++) {
a = Integer.invmod(i, p);
assert(a >= 0 && a < p);
assert((i * a) % p == 1);
}
assert(Integer.isPrime(2^107-1));
assert(!Integer.isPrime((2^107-1) * (2^89-1)));
a = Integer.factor((2^89-1)*2^3*11*13^2*1009);
assert(a == [ 2,2,2,11,13,13,1009,618970019642690137449562111 ]);
}
function test_mod()
{
var a, b, p;
a = Mod(3, 101);
b = Mod(-1, 101);
assert((a + b) == Mod(2, 101));
assert(a ^ 100 == Mod(1, 101));
p = 2 ^ 607 - 1; /* mersenne prime */
a = Mod(3, p) ^ (p - 1);
assert(a == Mod(1, p));
}
function test_polynomial()
{
var a, b, q, r, t, i;
a = (1 + X) ^ 4;
assert(a == X^4+4*X^3+6*X^2+4*X+1);
r = (1 + X);
q = (1+X+X^2);
b = (1 - X^2);
a = q * b + r;
t = Polynomial.divrem(a, b);
assert(t[0] == q);
assert(t[1] == r);
a = 1 + 2*X + 3*X^2;
assert(a.apply(0.1) == 1.23);
a = 1-2*X^2+2*X^3;
assert(deriv(a) == (6*X^2-4*X));
assert(deriv(integ(a)) == a);
a = (X-1)*(X-2)*(X-3)*(X-4)*(X-0.1);
r = polroots(a);
for(i = 0; i < r.length; i++) {
b = abs(a.apply(r[i]));
assert(b <= 1e-13);
}
}
function test_poly_mod()
{
var a, p;
/* modulo using polynomials */
p = X^2 + X + 1;
a = PolyMod(3+X, p) ^ 10;
assert(a == PolyMod(-3725*X-18357, p));
a = PolyMod(1/X, 1+X^2);
assert(a == PolyMod(-X, X^2+1));
}
function test_rfunc()
{
var a;
a = (X+1)/((X+1)*(X-1));
assert(a == 1/(X-1));
a = (X + 2) / (X - 2);
assert(a.apply(1/3) == -7/5);
assert(deriv((X^2-X+1)/(X-1)) == (X^2-2*X)/(X^2-2*X+1));
}
function test_series()
{
var a, b;
a = 1+X+O(X^5);
b = a.inverse();
assert(b == 1-X+X^2-X^3+X^4+O(X^5));
assert(deriv(b) == -1+2*X-3*X^2+4*X^3+O(X^4));
assert(deriv(integ(b)) == b);
a = Series(1/(1-X), 5);
assert(a == 1+X+X^2+X^3+X^4+O(X^5));
b = a.apply(0.1);
assert(b == 1.1111);
assert(exp(3*X^2+O(X^10)) == 1+3*X^2+9/2*X^4+9/2*X^6+27/8*X^8+O(X^10));
assert(sin(X+O(X^6)) == X-1/6*X^3+1/120*X^5+O(X^6));
assert(cos(X+O(X^6)) == 1-1/2*X^2+1/24*X^4+O(X^6));
assert(tan(X+O(X^8)) == X+1/3*X^3+2/15*X^5+17/315*X^7+O(X^8));
assert((1+X+O(X^6))^(2+X) == 1+2*X+2*X^2+3/2*X^3+5/6*X^4+5/12*X^5+O(X^6));
}
function test_matrix()
{
var a, b, r;
a = [[1, 2],[3, 4]];
b = [3, 4];
r = a * b;
assert(r == [11, 25]);
r = (a^-1) * 2;
assert(r == [[-4, 2],[3, -1]]);
assert(norm2([1,2,3]) == 14);
assert(diag([1,2,3]) == [ [ 1, 0, 0 ], [ 0, 2, 0 ], [ 0, 0, 3 ] ]);
assert(trans(a) == [ [ 1, 3 ], [ 2, 4 ] ]);
assert(trans([1,2,3]) == [[1,2,3]]);
assert(trace(a) == 5);
assert(charpoly(Matrix.hilbert(4)) == X^4-176/105*X^3+3341/12600*X^2-41/23625*X+1/6048000);
assert(det(Matrix.hilbert(4)) == 1/6048000);
a = [[1,2,1],[-2,-3,1],[3,5,0]];
assert(rank(a) == 2);
assert(ker(a) == [ [ 5 ], [ -3 ], [ 1 ] ]);
assert(dp([1, 2, 3], [3, -4, -7]) === -26);
assert(cp([1, 2, 3], [3, -4, -7]) == [ -2, 16, -10 ]);
}
function assert_eq(a, ref)
{
assert(abs(a / ref - 1.0) <= 1e-15);
}
function test_trig()
{
assert_eq(sin(1/2), 0.479425538604203);
assert_eq(sin(2+3*I), 9.154499146911428-4.168906959966565*I);
assert_eq(cos(2+3*I), -4.189625690968807-9.109227893755337*I);
assert_eq((2+0.5*I)^(1.1-0.5*I), 2.494363021357619-0.23076804554558092*I);
assert_eq(sqrt(2*I), 1 + I);
}
test_integer();
test_idiv();
test_float();
test_modulo();
test_mod();
test_polynomial();
test_poly_mod();
test_rfunc();
test_series();
test_matrix();
test_trig();

View File

@ -0,0 +1,119 @@
import * as bjson from "../bjson.so";
function assert(b, str)
{
if (b) {
return;
} else {
throw Error("assertion failed: " + str);
}
}
function toHex(a)
{
var i, s = "", tab, v;
tab = new Uint8Array(a);
for(i = 0; i < tab.length; i++) {
v = tab[i].toString(16);
if (v.length < 2)
v = "0" + v;
if (i !== 0)
s += " ";
s += v;
}
return s;
}
function toStr(a)
{
var s, i, props, prop;
switch(typeof(a)) {
case "object":
if (a === null)
return "null";
if (Array.isArray(a)) {
s = "[";
for(i = 0; i < a.length; i++) {
if (i != 0)
s += ",";
s += toStr(a[i]);
}
s += "]";
} else {
props = Object.keys(a);
s = "{";
for(i = 0; i < props.length; i++) {
if (i != 0)
s += ",";
prop = props[i];
s += prop + ":" + toStr(a[prop]);
}
s += "}";
}
return s;
case "undefined":
return "undefined";
case "string":
return a.__quote();
case "number":
case "bigfloat":
if (a == 0 && 1 / a < 0)
return "-0";
else
return a.toString();
break;
default:
return a.toString();
}
}
function bjson_test(a)
{
var buf, r, a_str, r_str;
a_str = toStr(a);
buf = bjson.write(a);
if (0) {
print(a_str, "->", toHex(buf));
}
r = bjson.read(buf, 0, buf.byteLength);
r_str = toStr(r);
if (a_str != r_str) {
print(a_str);
print(r_str);
assert(false);
}
}
function bjson_test_all()
{
var obj;
bjson_test({x:1, y:2, if:3});
bjson_test([1, 2, 3]);
bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]);
if (typeof BigInt !== "undefined") {
bjson_test([BigInt("1"), -BigInt("0x123456789"),
BigInt("0x123456789abcdef123456789abcdef")]);
}
if (typeof BigFloat !== "undefined") {
BigFloatEnv.setPrec(function () {
bjson_test([BigFloat("0.1"), BigFloat("-1e30"), BigFloat("0"),
BigFloat("-0"), BigFloat("Infinity"), BigFloat("-Infinity"),
0.0 / BigFloat("0"), BigFloat.MAX_VALUE,
BigFloat.MIN_VALUE]);
}, 113, 15);
}
/* tested with a circular reference */
obj = {};
obj.x = obj;
try {
bjson.write(obj);
assert(false);
} catch(e) {
assert(e instanceof TypeError);
}
}
bjson_test_all();

View File

@ -0,0 +1,598 @@
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function my_func(a, b)
{
return a + b;
}
function test_function()
{
function f(a, b) {
var i, tab = [];
tab.push(this);
for(i = 0; i < arguments.length; i++)
tab.push(arguments[i]);
return tab;
}
function constructor1(a) {
this.x = a;
}
var r, g;
r = my_func.call(null, 1, 2);
assert(r, 3, "call");
r = my_func.apply(null, [1, 2]);
assert(r, 3, "apply");
r = new Function("a", "b", "return a + b;");
assert(r(2,3), 5, "function");
g = f.bind(1, 2);
assert(g.length, 1);
assert(g.name, "bound f");
assert(g(3), [1,2,3]);
g = constructor1.bind(null, 1);
r = new g();
assert(r.x, 1);
}
function test()
{
var r, a, b, c, err;
r = Error("hello");
assert(r.message, "hello", "Error");
a = new Object();
a.x = 1;
assert(a.x, 1, "Object");
assert(Object.getPrototypeOf(a), Object.prototype, "getPrototypeOf");
Object.defineProperty(a, "y", { value: 3, writable: true, configurable: true, enumerable: true });
assert(a.y, 3, "defineProperty");
Object.defineProperty(a, "z", { get: function () { return 4; }, set: function(val) { this.z_val = val; }, configurable: true, enumerable: true });
assert(a.z, 4, "get");
a.z = 5;
assert(a.z_val, 5, "set");
a = { get z() { return 4; }, set z(val) { this.z_val = val; } };
assert(a.z, 4, "get");
a.z = 5;
assert(a.z_val, 5, "set");
b = Object.create(a);
assert(Object.getPrototypeOf(b), a, "create");
c = {u:2};
/* XXX: refcount bug in 'b' instead of 'a' */
Object.setPrototypeOf(a, c);
assert(Object.getPrototypeOf(a), c, "setPrototypeOf");
a = {};
assert(a.toString(), "[object Object]", "toString");
a = {x:1};
assert(Object.isExtensible(a), true, "extensible");
Object.preventExtensions(a);
err = false;
try {
a.y = 2;
} catch(e) {
err = true;
}
assert(Object.isExtensible(a), false, "extensible");
assert(typeof a.y, "undefined", "extensible");
assert(err, true, "extensible");
}
function test_enum()
{
var a, tab;
a = {x:1,
"18014398509481984": 1,
"9007199254740992": 1,
"9007199254740991": 1,
"4294967296": 1,
"4294967295": 1,
y:1,
"4294967294": 1,
"1": 2};
tab = Object.keys(a);
// console.log("tab=" + tab.toString());
assert(tab, ["1","4294967294","4294967295","4294967296","9007199254740991","x","18014398509481984","9007199254740992","y"], "keys");
}
function test_array()
{
var a, err;
a = [1, 2, 3];
assert(a.length, 3, "array");
assert(a[2], 3, "array1");
a = new Array(10);
assert(a.length, 10, "array2");
a = new Array(1, 2);
assert(a.length === 2 && a[0] === 1 && a[1] === 2, true, "array3");
a = [1, 2, 3];
a.length = 2;
assert(a.length === 2 && a[0] === 1 && a[1] === 2, true, "array4");
a = [];
a[1] = 10;
a[4] = 3;
assert(a.length, 5);
a = [1,2];
a.length = 5;
a[4] = 1;
a.length = 4;
assert(a[4] !== 1, true, "array5");
a = [1,2];
a.push(3,4);
assert(a.join(), "1,2,3,4", "join");
a = [1,2,3,4,5];
Object.defineProperty(a, "3", { configurable: false });
err = false;
try {
a.length = 2;
} catch(e) {
err = true;
}
assert(err && a.toString() === "1,2,3,4");
}
function test_string()
{
var a;
a = String("abc");
assert(a.length, 3, "string");
assert(a[1], "b", "string");
assert(a.charCodeAt(1), 0x62, "string");
assert(String.fromCharCode(65), "A", "string");
assert(String.fromCharCode.apply(null, [65, 66, 67]), "ABC", "string");
assert(a.charAt(1), "b");
assert(a.charAt(-1), "");
assert(a.charAt(3), "");
a = "abcd";
assert(a.substring(1, 3), "bc", "substring");
a = String.fromCharCode(0x20ac);
assert(a.charCodeAt(0), 0x20ac, "unicode");
assert(a, "€", "unicode");
assert(a, "\u20ac", "unicode");
assert(a, "\u{20ac}", "unicode");
assert("a", "\x61", "unicode");
a = "\u{10ffff}";
assert(a.length, 2, "unicode");
assert(a, "\u{dbff}\u{dfff}", "unicode");
assert(a.codePointAt(0), 0x10ffff);
assert(String.fromCodePoint(0x10ffff), a);
assert("a".concat("b", "c"), "abc");
assert("abcabc".indexOf("cab"), 2);
assert("abcabc".indexOf("cab2"), -1);
assert("abc".indexOf("c"), 2);
assert("aaa".indexOf("a"), 0);
assert("aaa".indexOf("a", NaN), 0);
assert("aaa".indexOf("a", -Infinity), 0);
assert("aaa".indexOf("a", -1), 0);
assert("aaa".indexOf("a", -0), 0);
assert("aaa".indexOf("a", 0), 0);
assert("aaa".indexOf("a", 1), 1);
assert("aaa".indexOf("a", 2), 2);
assert("aaa".indexOf("a", 3), -1);
assert("aaa".indexOf("a", 4), -1);
assert("aaa".indexOf("a", Infinity), -1);
assert("aaa".indexOf(""), 0);
assert("aaa".indexOf("", NaN), 0);
assert("aaa".indexOf("", -Infinity), 0);
assert("aaa".indexOf("", -1), 0);
assert("aaa".indexOf("", -0), 0);
assert("aaa".indexOf("", 0), 0);
assert("aaa".indexOf("", 1), 1);
assert("aaa".indexOf("", 2), 2);
assert("aaa".indexOf("", 3), 3);
assert("aaa".indexOf("", 4), 3);
assert("aaa".indexOf("", Infinity), 3);
assert("aaa".lastIndexOf("a"), 2);
assert("aaa".lastIndexOf("a", NaN), 2);
assert("aaa".lastIndexOf("a", -Infinity), 0);
assert("aaa".lastIndexOf("a", -1), 0);
assert("aaa".lastIndexOf("a", -0), 0);
assert("aaa".lastIndexOf("a", 0), 0);
assert("aaa".lastIndexOf("a", 1), 1);
assert("aaa".lastIndexOf("a", 2), 2);
assert("aaa".lastIndexOf("a", 3), 2);
assert("aaa".lastIndexOf("a", 4), 2);
assert("aaa".lastIndexOf("a", Infinity), 2);
assert("aaa".lastIndexOf(""), 3);
assert("aaa".lastIndexOf("", NaN), 3);
assert("aaa".lastIndexOf("", -Infinity), 0);
assert("aaa".lastIndexOf("", -1), 0);
assert("aaa".lastIndexOf("", -0), 0);
assert("aaa".lastIndexOf("", 0), 0);
assert("aaa".lastIndexOf("", 1), 1);
assert("aaa".lastIndexOf("", 2), 2);
assert("aaa".lastIndexOf("", 3), 3);
assert("aaa".lastIndexOf("", 4), 3);
assert("aaa".lastIndexOf("", Infinity), 3);
assert("a,b,c".split(","), ["a","b","c"]);
assert(",b,c".split(","), ["","b","c"]);
assert("a,b,".split(","), ["a","b",""]);
assert("aaaa".split(), [ "aaaa" ]);
assert("aaaa".split(undefined, 0), [ ]);
assert("aaaa".split(""), [ "a", "a", "a", "a" ]);
assert("aaaa".split("", 0), [ ]);
assert("aaaa".split("", 1), [ "a" ]);
assert("aaaa".split("", 2), [ "a", "a" ]);
assert("aaaa".split("a"), [ "", "", "", "", "" ]);
assert("aaaa".split("a", 2), [ "", "" ]);
assert("aaaa".split("aa"), [ "", "", "" ]);
assert("aaaa".split("aa", 0), [ ]);
assert("aaaa".split("aa", 1), [ "" ]);
assert("aaaa".split("aa", 2), [ "", "" ]);
assert("aaaa".split("aaa"), [ "", "a" ]);
assert("aaaa".split("aaaa"), [ "", "" ]);
assert("aaaa".split("aaaaa"), [ "aaaa" ]);
assert("aaaa".split("aaaaa", 0), [ ]);
assert("aaaa".split("aaaaa", 1), [ "aaaa" ]);
assert(eval('"\0"'), "\0");
}
function test_math()
{
var a;
a = 1.4;
assert(Math.floor(a), 1);
assert(Math.ceil(a), 2);
assert(Math.imul(0x12345678, 123), -1088058456);
assert(Math.fround(0.1), 0.10000000149011612);
}
function test_number()
{
assert(parseInt("123"), 123);
assert(parseInt(" 123r"), 123);
assert(parseInt("0x123"), 0x123);
assert(parseInt("0o123"), 0);
assert(+" 123 ", 123);
assert(+"0b111", 7);
assert(+"0o123", 83);
assert(parseFloat("0x1234"), 0);
assert(parseFloat("Infinity"), Infinity);
assert(parseFloat("-Infinity"), -Infinity);
assert(parseFloat("123.2"), 123.2);
assert(parseFloat("123.2e3"), 123200);
assert((25).toExponential(0), "3e+1");
assert((-25).toExponential(0), "-3e+1");
assert((2.5).toPrecision(1), "3");
assert((-2.5).toPrecision(1), "-3");
assert((1.125).toFixed(2), "1.13");
assert((-1.125).toFixed(2), "-1.13");
}
function test_eval()
{
function f(b) {
var x = 1;
return eval(b);
}
var r, a;
r = eval("1+1;");
assert(r, 2, "eval");
r = eval("var my_var=2; my_var;");
assert(r, 2, "eval");
assert(typeof my_var, "undefined");
assert(eval("if (1) 2; else 3;"), 2);
assert(eval("if (0) 2; else 3;"), 3);
assert(f.call(1, "this"), 1);
a = 2;
assert(eval("a"), 2);
eval("a = 3");
assert(a, 3);
assert(f("arguments.length", 1), 2);
assert(f("arguments[1]", 1), 1);
a = 4;
assert(f("a"), 4);
f("a=3");
assert(a, 3);
}
function test_typed_array()
{
var buffer, a, i;
a = new Uint8Array(4);
assert(a.length, 4);
for(i = 0; i < a.length; i++)
a[i] = i;
assert(a.join(","), "0,1,2,3");
a[0] = -1;
assert(a[0], 255);
a = new Int8Array(3);
a[0] = 255;
assert(a[0], -1);
a = new Int32Array(3);
a[0] = Math.pow(2, 32) - 1;
assert(a[0], -1);
assert(a.BYTES_PER_ELEMENT, 4);
a = new Uint8ClampedArray(4);
a[0] = -100;
a[1] = 1.5;
a[2] = 0.5;
a[3] = 1233.5;
assert(a.toString(), "0,2,0,255");
buffer = new ArrayBuffer(16);
assert(buffer.byteLength, 16);
a = new Uint32Array(buffer, 12, 1);
assert(a.length, 1);
a[0] = -1;
a = new Uint16Array(buffer, 2);
a[0] = -1;
a = new Float32Array(buffer, 8, 1);
a[0] = 1;
a = new Uint8Array(buffer);
assert(a.toString(), "0,0,255,255,0,0,0,0,0,0,128,63,255,255,255,255");
assert(a.buffer, buffer);
a = new Uint8Array([1, 2, 3, 4]);
assert(a.toString(), "1,2,3,4");
a.set([10, 11], 2);
assert(a.toString(), "1,2,10,11");
}
function test_json()
{
var a, s;
s = '{"x":1,"y":true,"z":null,"a":[1,2,3],"s":"str"}';
a = JSON.parse(s);
assert(a.x, 1);
assert(a.y, true);
assert(a.z, null);
assert(JSON.stringify(a), s);
}
function test_date()
{
var d = new Date(1506098258091), a;
assert(d.toISOString(), "2017-09-22T16:37:38.091Z");
d.setUTCHours(18, 10, 11);
assert(d.toISOString(), "2017-09-22T18:10:11.091Z");
a = Date.parse(d.toISOString());
assert((new Date(a)).toISOString(), d.toISOString());
}
function test_regexp()
{
var a, str;
str = "abbbbbc";
a = /(b+)c/.exec(str);
assert(a[0], "bbbbbc");
assert(a[1], "bbbbb");
assert(a.index, 1);
assert(a.input, str);
a = /(b+)c/.test(str);
assert(a, true);
assert(/\x61/.exec("a")[0], "a");
assert(/\u0061/.exec("a")[0], "a");
assert(/\ca/.exec("\x01")[0], "\x01");
assert(/\\a/.exec("\\a")[0], "\\a");
assert(/\c0/.exec("\\c0")[0], "\\c0");
a = /(\.(?=com|org)|\/)/.exec("ah.com");
assert(a.index === 2 && a[0] === ".");
a = /(\.(?!com|org)|\/)/.exec("ah.com");
assert(a, null);
a = /(?=(a+))/.exec("baaabac");
assert(a.index === 1 && a[0] === "" && a[1] === "aaa");
a = /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac");
assert(a, ["zaacbbbcac","z","ac","a",,"c"]);
a = eval("/\0a/");
assert(a.toString(), "/\0a/");
assert(a.exec("\0a")[0], "\0a");
}
function test_symbol()
{
var a, b, obj, c;
a = Symbol("abc");
obj = {};
obj[a] = 2;
assert(obj[a], 2);
assert(typeof obj["abc"], "undefined");
assert(String(a), "Symbol(abc)");
b = Symbol("abc");
assert(a == a);
assert(a === a);
assert(a != b);
assert(a !== b);
b = Symbol.for("abc");
c = Symbol.for("abc");
assert(b === c);
assert(b !== a);
assert(Symbol.keyFor(b), "abc");
assert(Symbol.keyFor(a), undefined);
a = Symbol("aaa");
assert(a.valueOf(), a);
assert(a.toString(), "Symbol(aaa)");
b = Object(a);
assert(b.valueOf(), a);
assert(b.toString(), "Symbol(aaa)");
}
function test_map()
{
var a, i, n, tab, o, v;
n = 1000;
a = new Map();
tab = [];
for(i = 0; i < n; i++) {
v = { };
o = { id: i };
tab[i] = [o, v];
a.set(o, v);
}
assert(a.size, n);
for(i = 0; i < n; i++) {
assert(a.get(tab[i][0]), tab[i][1]);
}
i = 0;
a.forEach(function (v, o) {
assert(o, tab[i++][0]);
assert(a.has(o));
assert(a.delete(o));
assert(!a.has(o));
});
assert(a.size, 0);
}
function test_weak_map()
{
var a, i, n, tab, o, v, n2;
a = new WeakMap();
n = 10;
tab = [];
for(i = 0; i < n; i++) {
v = { };
o = { id: i };
tab[i] = [o, v];
a.set(o, v);
}
o = null;
n2 = n >> 1;
for(i = 0; i < n2; i++) {
a.delete(tab[i][0]);
}
for(i = n2; i < n; i++) {
tab[i][0] = null; /* should remove the object from the WeakMap too */
}
/* the WeakMap should be empty here */
}
function test_generator()
{
function *f() {
var ret;
yield 1;
ret = yield 2;
assert(ret, "next_arg");
return 3;
}
function *f2() {
yield 1;
yield 2;
return "ret_val";
}
function *f1() {
var ret = yield *f2();
assert(ret, "ret_val");
return 3;
}
var g, v;
g = f();
v = g.next();
assert(v.value === 1 && v.done === false);
v = g.next();
assert(v.value === 2 && v.done === false);
v = g.next("next_arg");
assert(v.value === 3 && v.done === true);
v = g.next();
assert(v.value === undefined && v.done === true);
g = f1();
v = g.next();
assert(v.value === 1 && v.done === false);
v = g.next();
assert(v.value === 2 && v.done === false);
v = g.next();
assert(v.value === 3 && v.done === true);
v = g.next();
assert(v.value === undefined && v.done === true);
}
test();
test_function();
test_enum();
test_array();
test_string();
test_math();
test_number();
test_eval();
test_typed_array();
test_json();
test_date();
test_regexp();
test_symbol();
test_map();
test_weak_map();
test_generator();

View File

@ -0,0 +1,221 @@
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
var log_str = "";
function log(str)
{
log_str += str + ",";
}
function f(a, b, c)
{
var x = 10;
log("a="+a);
function g(d) {
function h() {
log("d=" + d);
log("x=" + x);
}
log("b=" + b);
log("c=" + c);
h();
}
g(4);
return g;
}
var g1 = f(1, 2, 3);
g1(5);
assert(log_str, "a=1,b=2,c=3,d=4,x=10,b=2,c=3,d=5,x=10,", "closure1");
function test_closure1()
{
function f2()
{
var val = 1;
function set(a) {
val = a;
}
function get(a) {
return val;
}
return { "set": set, "get": get };
}
var obj = f2();
obj.set(10);
var r;
r = obj.get();
assert(r, 10, "closure2");
}
function test_closure2()
{
var expr_func = function myfunc1(n) {
function myfunc2(n) {
return myfunc1(n - 1);
}
if (n == 0)
return 0;
else
return myfunc2(n);
};
var r;
r = expr_func(1);
assert(r, 0, "expr_func");
}
function test_closure3()
{
function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
var fib_func = function fib1(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib1(n - 1) + fib1(n - 2);
};
assert(fib(6), 8, "fib");
assert(fib_func(6), 8, "fib_func");
}
function test_arrow_function()
{
"use strict";
function f1() {
return (() => arguments)();
}
function f2() {
return (() => this)();
}
function f3() {
return (() => eval("this"))();
}
function f4() {
return (() => eval("new.target"))();
}
var a;
a = f1(1, 2);
assert(a.length, 2);
assert(a[0] === 1 && a[1] === 2);
assert(f2.call("this_val") === "this_val");
assert(f3.call("this_val") === "this_val");
assert(new f4() === f4);
var o1 = { f() { return this; } };
var o2 = { f() {
return (() => eval("super.f()"))();
} };
o2.__proto__ = o1;
assert(o2.f() === o2);
}
function test_with()
{
var o1 = { x: "o1", y: "o1" };
var x = "local";
eval('var z="var_obj";');
assert(z === "var_obj");
with (o1) {
assert(x === "o1");
assert(eval("x") === "o1");
var f = function () {
o2 = { x: "o2" };
with (o2) {
assert(x === "o2");
assert(y === "o1");
assert(z === "var_obj");
assert(eval("x") === "o2");
assert(eval("y") === "o1");
assert(eval("z") === "var_obj");
assert(eval('eval("x")') === "o2");
}
};
f();
}
}
function test_eval_closure()
{
var tab;
tab = [];
for(let i = 0; i < 3; i++) {
eval("tab.push(function g1() { return i; })");
}
for(let i = 0; i < 3; i++) {
assert(tab[i]() === i);
}
tab = [];
for(let i = 0; i < 3; i++) {
let f = function f() {
eval("tab.push(function g2() { return i; })");
};
f();
}
for(let i = 0; i < 3; i++) {
assert(tab[i]() === i);
}
}
function test_eval_const()
{
const a = 1;
var success = false;
var f = function () {
eval("a = 1");
};
try {
f();
} catch(e) {
success = (e instanceof TypeError);
}
assert(success);
}
test_closure1();
test_closure2();
test_closure3();
test_arrow_function();
test_with();
test_eval_closure();
test_eval_const();

View File

@ -0,0 +1,368 @@
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function test_while()
{
var i, c;
i = 0;
c = 0;
while (i < 3) {
c++;
i++;
}
assert(c === 3);
}
function test_while_break()
{
var i, c;
i = 0;
c = 0;
while (i < 3) {
c++;
if (i == 1)
break;
i++;
}
assert(c === 2 && i === 1);
}
function test_do_while()
{
var i, c;
i = 0;
c = 0;
do {
c++;
i++;
} while (i < 3);
assert(c === 3 && i === 3);
}
function test_for()
{
var i, c;
c = 0;
for(i = 0; i < 3; i++) {
c++;
}
assert(c === 3 && i === 3);
c = 0;
for(var j = 0; j < 3; j++) {
c++;
}
assert(c === 3 && j === 3);
}
function test_for_in()
{
var i, tab, a, b;
tab = [];
for(i in {x:1, y: 2}) {
tab.push(i);
}
assert(tab.toString(), "x,y", "for_in");
/* prototype chain test */
a = {x:2, y: 2, "1": 3};
b = {"4" : 3 };
Object.setPrototypeOf(a, b);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "1,x,y,4", "for_in");
/* non enumerable properties hide enumerables ones in the
prototype chain */
a = {y: 2, "1": 3};
Object.defineProperty(a, "x", { value: 1 });
b = {"x" : 3 };
Object.setPrototypeOf(a, b);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "1,y", "for_in");
/* array optimization */
a = [];
for(i = 0; i < 10; i++)
a.push(i);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "0,1,2,3,4,5,6,7,8,9", "for_in");
/* iterate with a field */
a={x:0};
tab = [];
for(a.x in {x:1, y: 2}) {
tab.push(a.x);
}
assert(tab.toString(), "x,y", "for_in");
/* iterate with a variable field */
a=[0];
tab = [];
for(a[0] in {x:1, y: 2}) {
tab.push(a[0]);
}
assert(tab.toString(), "x,y", "for_in");
/* variable definition in the for in */
tab = [];
for(var j in {x:1, y: 2}) {
tab.push(j);
}
assert(tab.toString(), "x,y", "for_in");
/* variable assigment in the for in */
tab = [];
for(var k = 2 in {x:1, y: 2}) {
tab.push(k);
}
assert(tab.toString(), "x,y", "for_in");
}
function test_for_in2()
{
var i;
tab = [];
for(i in {x:1, y: 2, z:3}) {
if (i === "y")
continue;
tab.push(i);
}
assert(tab.toString() == "x,z");
tab = [];
for(i in {x:1, y: 2, z:3}) {
if (i === "z")
break;
tab.push(i);
}
assert(tab.toString() == "x,y");
}
function test_for_break()
{
var i, c;
c = 0;
L1: for(i = 0; i < 3; i++) {
c++;
if (i == 0)
continue;
while (1) {
break L1;
}
}
assert(c === 2 && i === 1);
}
function test_switch1()
{
var i, a, s;
s = "";
for(i = 0; i < 3; i++) {
a = "?";
switch(i) {
case 0:
a = "a";
break;
case 1:
a = "b";
break;
default:
a = "c";
break;
}
s += a;
}
assert(s === "abc" && i === 3);
}
function test_switch2()
{
var i, a, s;
s = "";
for(i = 0; i < 4; i++) {
a = "?";
switch(i) {
case 0:
a = "a";
break;
case 1:
a = "b";
break;
case 2:
continue;
default:
a = "" + i;
break;
}
s += a;
}
assert(s === "ab3" && i === 4);
}
function test_try_catch1()
{
try {
throw "hello";
} catch (e) {
assert(e, "hello", "catch");
return;
}
assert(false, "catch");
}
function test_try_catch2()
{
var a;
try {
a = 1;
} catch (e) {
a = 2;
}
assert(a, 1, "catch");
}
function test_try_catch3()
{
var s;
s = "";
try {
s += "t";
} catch (e) {
s += "c";
} finally {
s += "f";
}
assert(s, "tf", "catch");
}
function test_try_catch4()
{
var s;
s = "";
try {
s += "t";
throw "c";
} catch (e) {
s += e;
} finally {
s += "f";
}
assert(s, "tcf", "catch");
}
function test_try_catch5()
{
var s;
s = "";
for(;;) {
try {
s += "t";
break;
s += "b";
} finally {
s += "f";
}
}
assert(s, "tf", "catch");
}
function test_try_catch6()
{
function f() {
try {
s += 't';
return 1;
} finally {
s += "f";
}
}
var s = "";
assert(f() === 1);
assert(s, "tf", "catch6");
}
function test_try_catch7()
{
var s;
s = "";
try {
try {
s += "t";
throw "a";
} finally {
s += "f";
}
} catch(e) {
s += e;
} finally {
s += "g";
}
assert(s, "tfag", "catch");
}
function test_try_catch8()
{
var i, s;
s = "";
for(var i in {x:1, y:2}) {
try {
s += i;
throw "a";
} catch (e) {
s += e;
} finally {
s += "f";
}
}
assert(s === "xafyaf");
}
test_while();
test_while_break();
test_do_while();
test_for();
test_for_break();
test_switch1();
test_switch2();
test_for_in();
test_for_in2();
test_try_catch1();
test_try_catch2();
test_try_catch3();
test_try_catch4();
test_try_catch5();
test_try_catch6();
test_try_catch7();
test_try_catch8();

View File

@ -0,0 +1,302 @@
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function test_op1()
{
var r, a;
r = 1 + 2;
assert(r, 3, "1 + 2 === 3");
r = 1 - 2;
assert(r, -1, "1 - 2 === -1");
r = -1;
assert(r, -1, "-1 === -1");
r = +2;
assert(r, 2, "+2 === 2");
r = 2 * 3;
assert(r, 6, "2 * 3 === 6");
r = 4 / 2;
assert(r, 2, "4 / 2 === 2");
r = 4 % 3;
assert(r, 1, "4 % 3 === 3");
r = 4 << 2;
assert(r, 16, "4 << 2 === 16");
r = 1 << 0;
assert(r, 1, "1 << 0 === 1");
r = 1 << 31;
assert(r, -2147483648, "1 << 31 === -2147483648");
r = 1 << 32;
assert(r, 1, "1 << 32 === 1");
r = (1 << 31) < 0;
assert(r, true, "(1 << 31) < 0 === true");
r = -4 >> 1;
assert(r, -2, "-4 >> 1 === -2");
r = -4 >>> 1;
assert(r, 0x7ffffffe, "-4 >>> 1 === 0x7ffffffe");
r = 1 & 1;
assert(r, 1, "1 & 1 === 1");
r = 0 | 1;
assert(r, 1, "0 | 1 === 1");
r = 1 ^ 1;
assert(r, 0, "1 ^ 1 === 0");
r = ~1;
assert(r, -2, "~1 === -2");
r = !1;
assert(r, false, "!1 === false");
assert((1 < 2), true, "(1 < 2) === true");
assert((2 > 1), true, "(2 > 1) === true");
assert(('b' > 'a'), true, "('b' > 'a') === true");
assert(2 ** 8, 256, "2 ** 8 === 256");
}
function test_cvt()
{
assert((NaN | 0) === 0);
assert((Infinity | 0) === 0);
assert(((-Infinity) | 0) === 0);
assert(("12345" | 0) === 12345);
assert(("0x12345" | 0) === 0x12345);
assert(((4294967296 * 3 - 4) | 0) === -4);
assert(("12345" >>> 0) === 12345);
assert(("0x12345" >>> 0) === 0x12345);
assert((NaN >>> 0) === 0);
assert((Infinity >>> 0) === 0);
assert(((-Infinity) >>> 0) === 0);
assert(((4294967296 * 3 - 4) >>> 0) === (4294967296 - 4));
}
function test_eq()
{
assert(null == undefined);
assert(undefined == null);
assert(true == 1);
assert(0 == false);
assert("" == 0);
assert("123" == 123);
assert("122" != 123);
assert((new Number(1)) == 1);
assert(2 == (new Number(2)));
assert((new String("abc")) == "abc");
assert({} != "abc");
}
function test_inc_dec()
{
var a, r;
a = 1;
r = a++;
assert(r === 1 && a === 2, true, "++");
a = 1;
r = ++a;
assert(r === 2 && a === 2, true, "++");
a = 1;
r = a--;
assert(r === 1 && a === 0, true, "--");
a = 1;
r = --a;
assert(r === 0 && a === 0, true, "--");
a = {x:true};
a.x++;
assert(a.x, 2, "++");
a = {x:true};
a.x--;
assert(a.x, 0, "--");
a = [true];
a[0]++;
assert(a[0], 2, "++");
a = {x:true};
r = a.x++;
assert(r === 1 && a.x === 2, true, "++");
a = {x:true};
r = a.x--;
assert(r === 1 && a.x === 0, true, "--");
a = [true];
r = a[0]++;
assert(r === 1 && a[0] === 2, true, "++");
a = [true];
r = a[0]--;
assert(r === 1 && a[0] === 0, true, "--");
}
function F(x)
{
this.x = x;
}
function test_op2()
{
var a, b;
a = new Object;
a.x = 1;
assert(a.x, 1, "new");
b = new F(2);
assert(b.x, 2, "new");
a = {x : 2};
assert(("x" in a), true, "in");
assert(("y" in a), false, "in");
a = {};
assert((a instanceof Object), true, "instanceof");
assert((a instanceof String), false, "instanceof");
assert((typeof 1), "number", "typeof");
assert((typeof Object), "function", "typeof");
assert((typeof null), "object", "typeof");
assert((typeof unknown_var), "undefined", "typeof");
a = {x: 1, y: 1};
assert((delete a.x), true, "delete");
assert(("x" in a), false, "delete");
a = {x: 1, if: 2};
assert(a.if === 2);
}
function test_prototype()
{
function f() { }
assert(f.prototype.constructor, f, "prototype");
}
function test_arguments()
{
function f2() {
assert(arguments.length, 2, "arguments");
assert(arguments[0], 1, "arguments");
assert(arguments[1], 3, "arguments");
}
f2(1, 3);
}
function test_class()
{
var o;
class C {
constructor() {
this.x = 10;
}
f() {
return 1;
}
static F() {
return -1;
}
get y() {
return 12;
}
};
class D extends C {
constructor() {
super();
this.z = 20;
}
g() {
return 2;
}
static G() {
return -2;
}
h() {
return super.f();
}
static H() {
return super["F"]();
}
}
assert(C.F() === -1);
assert(Object.getOwnPropertyDescriptor(C.prototype, "y").get.name === "get y");
o = new C();
assert(o.f() === 1);
assert(o.x === 10);
assert(D.F() === -1);
assert(D.G() === -2);
assert(D.H() === -1);
o = new D();
assert(o.f() === 1);
assert(o.g() === 2);
assert(o.x === 10);
assert(o.z === 20);
assert(o.h() === 1);
/* test class name scope */
var E1 = class E { static F() { return E; } };
assert(E1 === E1.F());
};
function test_template()
{
var a, b;
b = 123;
a = `abc${b}d`;
assert(a === "abc123d");
a = String.raw `abc${b}d`;
assert(a === "abc123d");
}
test_op1();
test_cvt();
test_eq();
test_inc_dec();
test_op2();
test_prototype();
test_arguments();
test_class();
test_template();

View File

@ -0,0 +1,147 @@
import * as std from "std";
import * as os from "os";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
// load more elaborate version of assert if available
try { std.loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function test_printf()
{
assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc");
}
function test_file1()
{
var f, len, str, size, buf, ret, i, str1;
f = std.tmpfile();
str = "hello world\n";
f.puts(str);
f.seek(0, std.SEEK_SET);
str1 = f.readAsString();
assert(str1 === str);
f.seek(0, std.SEEK_END);
size = f.tell();
assert(size === str.length);
f.seek(0, std.SEEK_SET);
buf = new Uint8Array(size);
ret = f.read(buf.buffer, 0, size);
assert(ret === size);
for(i = 0; i < size; i++)
assert(buf[i] === str.charCodeAt(i));
f.close();
}
function test_file2()
{
var f, str, i, size;
f = std.tmpfile();
str = "hello world\n";
size = str.length;
for(i = 0; i < size; i++)
f.putByte(str.charCodeAt(i));
f.seek(0, std.SEEK_SET);
for(i = 0; i < size; i++) {
assert(str.charCodeAt(i) === f.getByte());
}
assert(f.getByte() === -1);
f.close();
}
function test_getline()
{
var f, line, line_count, lines, i;
lines = ["hello world", "line 1", "line 2" ];
f = std.tmpfile();
for(i = 0; i < lines.length; i++) {
f.puts(lines[i], "\n");
}
f.seek(0, std.SEEK_SET);
assert(!f.eof());
line_count = 0;
for(;;) {
line = f.getline();
if (line === null)
break;
assert(line == lines[line_count]);
line_count++;
}
assert(f.eof());
assert(line_count === lines.length);
f.close();
}
function test_os()
{
var fd, fname, buf, buf2, i;
assert(os.isatty(0));
fname = "tmp_file.txt";
fd = os.open(fname, os.O_RDWR | os.O_CREAT | os.O_TRUNC);
assert(fd >= 0);
buf = new Uint8Array(10);
for(i = 0; i < buf.length; i++)
buf[i] = i;
assert(os.write(fd, buf.buffer, 0, buf.length) === buf.length);
assert(os.seek(fd, 0, os.SEEK_SET) === 0);
buf2 = new Uint8Array(buf.length);
assert(os.read(fd, buf2.buffer, 0, buf2.length) === buf2.length);
for(i = 0; i < buf.length; i++)
assert(buf[i] == buf2[i]);
assert(os.close(fd) === 0);
assert(os.remove(fname) === 0);
fd = os.open(fname, os.O_RDONLY);
assert(fd < 0);
}
function test_timer()
{
var th, i;
/* just test that a timer can be inserted and removed */
th = [];
for(i = 0; i < 3; i++)
th[i] = os.setTimeout(1000, function () { });
for(i = 0; i < 3; i++)
os.clearTimeout(th[i]);
}
test_printf();
test_file1();
test_file2();
test_getline();
test_os();
test_timer();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,275 @@
#ifdef UNICODE_GENERAL_CATEGORY
DEF(Cn, "Unassigned") /* must be zero */
DEF(Lu, "Uppercase_Letter")
DEF(Ll, "Lowercase_Letter")
DEF(Lt, "Titlecase_Letter")
DEF(Lm, "Modifier_Letter")
DEF(Lo, "Other_Letter")
DEF(Mn, "Nonspacing_Mark")
DEF(Mc, "Spacing_Mark")
DEF(Me, "Enclosing_Mark")
DEF(Nd, "Decimal_Number,digit")
DEF(Nl, "Letter_Number")
DEF(No, "Other_Number")
DEF(Sm, "Math_Symbol")
DEF(Sc, "Currency_Symbol")
DEF(Sk, "Modifier_Symbol")
DEF(So, "Other_Symbol")
DEF(Pc, "Connector_Punctuation")
DEF(Pd, "Dash_Punctuation")
DEF(Ps, "Open_Punctuation")
DEF(Pe, "Close_Punctuation")
DEF(Pi, "Initial_Punctuation")
DEF(Pf, "Final_Punctuation")
DEF(Po, "Other_Punctuation")
DEF(Zs, "Space_Separator")
DEF(Zl, "Line_Separator")
DEF(Zp, "Paragraph_Separator")
DEF(Cc, "Control,cntrl")
DEF(Cf, "Format")
DEF(Cs, "Surrogate")
DEF(Co, "Private_Use")
/* synthetic properties */
DEF(LC, "Cased_Letter")
DEF(L, "Letter")
DEF(M, "Mark,Combining_Mark")
DEF(N, "Number")
DEF(S, "Symbol")
DEF(P, "Punctuation,punct")
DEF(Z, "Separator")
DEF(C, "Other")
#endif
#ifdef UNICODE_SCRIPT
DEF(Unknown, "Zzzz")
DEF(Adlam, "Adlm")
DEF(Ahom, "Ahom")
DEF(Anatolian_Hieroglyphs, "Hluw")
DEF(Arabic, "Arab")
DEF(Armenian, "Armn")
DEF(Avestan, "Avst")
DEF(Balinese, "Bali")
DEF(Bamum, "Bamu")
DEF(Bassa_Vah, "Bass")
DEF(Batak, "Batk")
DEF(Bengali, "Beng")
DEF(Bhaiksuki, "Bhks")
DEF(Bopomofo, "Bopo")
DEF(Brahmi, "Brah")
DEF(Braille, "Brai")
DEF(Buginese, "Bugi")
DEF(Buhid, "Buhd")
DEF(Canadian_Aboriginal, "Cans")
DEF(Carian, "Cari")
DEF(Caucasian_Albanian, "Aghb")
DEF(Chakma, "Cakm")
DEF(Cham, "Cham")
DEF(Cherokee, "Cher")
DEF(Common, "Zyyy")
DEF(Coptic, "Copt,Qaac")
DEF(Cuneiform, "Xsux")
DEF(Cypriot, "Cprt")
DEF(Cyrillic, "Cyrl")
DEF(Deseret, "Dsrt")
DEF(Devanagari, "Deva")
DEF(Dogra, "Dogr")
DEF(Duployan, "Dupl")
DEF(Egyptian_Hieroglyphs, "Egyp")
DEF(Elbasan, "Elba")
DEF(Ethiopic, "Ethi")
DEF(Georgian, "Geor")
DEF(Glagolitic, "Glag")
DEF(Gothic, "Goth")
DEF(Grantha, "Gran")
DEF(Greek, "Grek")
DEF(Gujarati, "Gujr")
DEF(Gunjala_Gondi, "Gong")
DEF(Gurmukhi, "Guru")
DEF(Han, "Hani")
DEF(Hangul, "Hang")
DEF(Hanifi_Rohingya, "Rohg")
DEF(Hanunoo, "Hano")
DEF(Hatran, "Hatr")
DEF(Hebrew, "Hebr")
DEF(Hiragana, "Hira")
DEF(Imperial_Aramaic, "Armi")
DEF(Inherited, "Zinh,Qaai")
DEF(Inscriptional_Pahlavi, "Phli")
DEF(Inscriptional_Parthian, "Prti")
DEF(Javanese, "Java")
DEF(Kaithi, "Kthi")
DEF(Kannada, "Knda")
DEF(Katakana, "Kana")
DEF(Kayah_Li, "Kali")
DEF(Kharoshthi, "Khar")
DEF(Khmer, "Khmr")
DEF(Khojki, "Khoj")
DEF(Khudawadi, "Sind")
DEF(Lao, "Laoo")
DEF(Latin, "Latn")
DEF(Lepcha, "Lepc")
DEF(Limbu, "Limb")
DEF(Linear_A, "Lina")
DEF(Linear_B, "Linb")
DEF(Lisu, "Lisu")
DEF(Lycian, "Lyci")
DEF(Lydian, "Lydi")
DEF(Makasar, "Maka")
DEF(Mahajani, "Mahj")
DEF(Malayalam, "Mlym")
DEF(Mandaic, "Mand")
DEF(Manichaean, "Mani")
DEF(Marchen, "Marc")
DEF(Masaram_Gondi, "Gonm")
DEF(Medefaidrin, "Medf")
DEF(Meetei_Mayek, "Mtei")
DEF(Mende_Kikakui, "Mend")
DEF(Meroitic_Cursive, "Merc")
DEF(Meroitic_Hieroglyphs, "Mero")
DEF(Miao, "Plrd")
DEF(Modi, "Modi")
DEF(Mongolian, "Mong")
DEF(Mro, "Mroo")
DEF(Multani, "Mult")
DEF(Myanmar, "Mymr")
DEF(Nabataean, "Nbat")
DEF(New_Tai_Lue, "Talu")
DEF(Newa, "Newa")
DEF(Nko, "Nkoo")
DEF(Nushu, "Nshu")
DEF(Ogham, "Ogam")
DEF(Ol_Chiki, "Olck")
DEF(Old_Hungarian, "Hung")
DEF(Old_Italic, "Ital")
DEF(Old_North_Arabian, "Narb")
DEF(Old_Permic, "Perm")
DEF(Old_Persian, "Xpeo")
DEF(Old_Sogdian, "Sogo")
DEF(Old_South_Arabian, "Sarb")
DEF(Old_Turkic, "Orkh")
DEF(Oriya, "Orya")
DEF(Osage, "Osge")
DEF(Osmanya, "Osma")
DEF(Pahawh_Hmong, "Hmng")
DEF(Palmyrene, "Palm")
DEF(Pau_Cin_Hau, "Pauc")
DEF(Phags_Pa, "Phag")
DEF(Phoenician, "Phnx")
DEF(Psalter_Pahlavi, "Phlp")
DEF(Rejang, "Rjng")
DEF(Runic, "Runr")
DEF(Samaritan, "Samr")
DEF(Saurashtra, "Saur")
DEF(Sharada, "Shrd")
DEF(Shavian, "Shaw")
DEF(Siddham, "Sidd")
DEF(SignWriting, "Sgnw")
DEF(Sinhala, "Sinh")
DEF(Sogdian, "Sogd")
DEF(Sora_Sompeng, "Sora")
DEF(Soyombo, "Soyo")
DEF(Sundanese, "Sund")
DEF(Syloti_Nagri, "Sylo")
DEF(Syriac, "Syrc")
DEF(Tagalog, "Tglg")
DEF(Tagbanwa, "Tagb")
DEF(Tai_Le, "Tale")
DEF(Tai_Tham, "Lana")
DEF(Tai_Viet, "Tavt")
DEF(Takri, "Takr")
DEF(Tamil, "Taml")
DEF(Tangut, "Tang")
DEF(Telugu, "Telu")
DEF(Thaana, "Thaa")
DEF(Thai, "Thai")
DEF(Tibetan, "Tibt")
DEF(Tifinagh, "Tfng")
DEF(Tirhuta, "Tirh")
DEF(Ugaritic, "Ugar")
DEF(Vai, "Vaii")
DEF(Warang_Citi, "Wara")
DEF(Yi, "Yiii")
DEF(Zanabazar_Square, "Zanb")
#endif
#ifdef UNICODE_PROP_LIST
/* Prop list not exported to regexp */
DEF(Hyphen, "")
DEF(Other_Math, "")
DEF(Other_Alphabetic, "")
DEF(Other_Lowercase, "")
DEF(Other_Uppercase, "")
DEF(Other_Grapheme_Extend, "")
DEF(Other_Default_Ignorable_Code_Point, "")
DEF(Other_ID_Start, "")
DEF(Other_ID_Continue, "")
DEF(Prepended_Concatenation_Mark, "")
/* additional computed properties for smaller tables */
DEF(ID_Continue1, "")
DEF(XID_Start1, "")
DEF(XID_Continue1, "")
DEF(Changes_When_Titlecased1, "")
DEF(Changes_When_Casefolded1, "")
DEF(Changes_When_NFKC_Casefolded1, "")
/* Prop list exported to JS */
DEF(ASCII_Hex_Digit, "AHex")
DEF(Bidi_Control, "Bidi_C")
DEF(Dash, "")
DEF(Deprecated, "Dep")
DEF(Diacritic, "Dia")
DEF(Extender, "Ext")
DEF(Hex_Digit, "Hex")
DEF(IDS_Binary_Operator, "IDSB")
DEF(IDS_Trinary_Operator, "IDST")
DEF(Ideographic, "Ideo")
DEF(Join_Control, "Join_C")
DEF(Logical_Order_Exception, "LOE")
DEF(Noncharacter_Code_Point, "NChar")
DEF(Pattern_Syntax, "Pat_Syn")
DEF(Pattern_White_Space, "Pat_WS")
DEF(Quotation_Mark, "QMark")
DEF(Radical, "")
DEF(Regional_Indicator, "RI")
DEF(Sentence_Terminal, "STerm")
DEF(Soft_Dotted, "SD")
DEF(Terminal_Punctuation, "Term")
DEF(Unified_Ideograph, "UIdeo")
DEF(Variation_Selector, "VS")
DEF(White_Space, "space")
DEF(Bidi_Mirrored, "Bidi_M")
DEF(Emoji, "")
DEF(Emoji_Component, "")
DEF(Emoji_Modifier, "")
DEF(Emoji_Modifier_Base, "")
DEF(Emoji_Presentation, "")
DEF(Extended_Pictographic, "")
DEF(Default_Ignorable_Code_Point, "DI")
DEF(ID_Start, "IDS")
DEF(Case_Ignorable, "CI")
/* other binary properties */
DEF(ASCII,"")
DEF(Alphabetic, "Alpha")
DEF(Any, "")
DEF(Assigned,"")
DEF(Cased, "")
DEF(Changes_When_Casefolded, "CWCF")
DEF(Changes_When_Casemapped, "CWCM")
DEF(Changes_When_Lowercased, "CWL")
DEF(Changes_When_NFKC_Casefolded, "CWKCF")
DEF(Changes_When_Titlecased, "CWT")
DEF(Changes_When_Uppercased, "CWU")
DEF(Grapheme_Base, "Gr_Base")
DEF(Grapheme_Extend, "Gr_Ext")
DEF(ID_Continue, "IDC")
DEF(Lowercase, "Lower")
DEF(Math, "")
DEF(Uppercase, "Upper")
DEF(XID_Continue, "XIDC")
DEF(XID_Start, "XIDS")
/* internal tables with index */
DEF(Cased1, "")
#endif