aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy2
-rw-r--r--.github/actions/download-qt/action.yml2
-rw-r--r--.github/workflows/release.yml2
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt4
-rw-r--r--VERSION2
-rw-r--r--cmake/QbsBuildConfig.cmake4
-rw-r--r--doc/codeattributions.qdoc49
-rw-r--r--doc/qbs.qdoc6
-rw-r--r--docker/focal/Dockerfile2
-rw-r--r--docker/leap/Dockerfile2
-rw-r--r--docker/windowsservercore/Dockerfile4
-rw-r--r--qbs-resources/imports/QbsUnittest.qbs13
-rw-r--r--qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs4
-rw-r--r--scripts/address-sanitizer-suppressions.txt2
-rwxr-xr-xscripts/install-qt.sh2
-rw-r--r--share/qbs/modules/cpp/msvc.js4
-rw-r--r--share/qbs/modules/cpp/sdcc.js3
-rw-r--r--share/qbs/modules/cpp/windows-clang-cl.qbs3
-rw-r--r--share/qbs/modules/dmg/dmg.js6
-rw-r--r--share/qbs/modules/qbs/common.qbs5
-rw-r--r--src/lib/CMakeLists.txt4
-rw-r--r--src/lib/corelib/CMakeLists.txt7
-rw-r--r--src/lib/corelib/api/internaljobs.cpp3
-rw-r--r--src/lib/corelib/api/projectfileupdater.cpp2
-rw-r--r--src/lib/corelib/buildgraph/artifact.cpp2
-rw-r--r--src/lib/corelib/buildgraph/artifact.h5
-rw-r--r--src/lib/corelib/buildgraph/artifactsscriptvalue.cpp257
-rw-r--r--src/lib/corelib/buildgraph/artifactsscriptvalue.h13
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.cpp503
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.h13
-rw-r--r--src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp26
-rw-r--r--src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h7
-rw-r--r--src/lib/corelib/buildgraph/depscanner.cpp53
-rw-r--r--src/lib/corelib/buildgraph/depscanner.h7
-rw-r--r--src/lib/corelib/buildgraph/environmentscriptrunner.cpp44
-rw-r--r--src/lib/corelib/buildgraph/jscommandexecutor.cpp64
-rw-r--r--src/lib/corelib/buildgraph/processcommandexecutor.cpp47
-rw-r--r--src/lib/corelib/buildgraph/productbuilddata.cpp2
-rw-r--r--src/lib/corelib/buildgraph/productbuilddata.h3
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.cpp2
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.h6
-rw-r--r--src/lib/corelib/buildgraph/qtmocscanner.cpp57
-rw-r--r--src/lib/corelib/buildgraph/qtmocscanner.h14
-rw-r--r--src/lib/corelib/buildgraph/rulecommands.cpp318
-rw-r--r--src/lib/corelib/buildgraph/rulecommands.h19
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.cpp228
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.h12
-rw-r--r--src/lib/corelib/buildgraph/rulesevaluationcontext.cpp27
-rw-r--r--src/lib/corelib/buildgraph/rulesevaluationcontext.h13
-rw-r--r--src/lib/corelib/buildgraph/scriptclasspropertyiterator.h110
-rw-r--r--src/lib/corelib/buildgraph/transformer.cpp233
-rw-r--r--src/lib/corelib/buildgraph/transformer.h26
-rw-r--r--src/lib/corelib/corelib.qbs22
-rw-r--r--src/lib/corelib/jsextensions/binaryfile.cpp228
-rw-r--r--src/lib/corelib/jsextensions/domxml.cpp740
-rw-r--r--src/lib/corelib/jsextensions/environmentextension.cpp130
-rw-r--r--src/lib/corelib/jsextensions/file.cpp337
-rw-r--r--src/lib/corelib/jsextensions/fileinfoextension.cpp410
-rw-r--r--src/lib/corelib/jsextensions/host.cpp138
-rw-r--r--src/lib/corelib/jsextensions/jsextension.h337
-rw-r--r--src/lib/corelib/jsextensions/jsextensions.cpp21
-rw-r--r--src/lib/corelib/jsextensions/jsextensions.h14
-rw-r--r--src/lib/corelib/jsextensions/moduleproperties.cpp393
-rw-r--r--src/lib/corelib/jsextensions/moduleproperties.h25
-rw-r--r--src/lib/corelib/jsextensions/pkgconfigjs.cpp64
-rw-r--r--src/lib/corelib/jsextensions/pkgconfigjs.h27
-rw-r--r--src/lib/corelib/jsextensions/process.cpp306
-rw-r--r--src/lib/corelib/jsextensions/propertylist.cpp10
-rw-r--r--src/lib/corelib/jsextensions/propertylist_darwin.h91
-rw-r--r--src/lib/corelib/jsextensions/propertylist_darwin.mm285
-rw-r--r--src/lib/corelib/jsextensions/temporarydir.cpp85
-rw-r--r--src/lib/corelib/jsextensions/textfile.cpp201
-rw-r--r--src/lib/corelib/jsextensions/utilitiesextension.cpp724
-rw-r--r--src/lib/corelib/language/evaluationdata.h66
-rw-r--r--src/lib/corelib/language/evaluator.cpp818
-rw-r--r--src/lib/corelib/language/evaluator.h52
-rw-r--r--src/lib/corelib/language/evaluatorscriptclass.cpp776
-rwxr-xr-xsrc/lib/corelib/language/evaluatorscriptclass.h133
-rw-r--r--src/lib/corelib/language/language.cpp2
-rw-r--r--src/lib/corelib/language/language.h4
-rw-r--r--src/lib/corelib/language/loader.cpp13
-rw-r--r--src/lib/corelib/language/moduleloader.cpp50
-rw-r--r--src/lib/corelib/language/moduleproviderloader.cpp12
-rw-r--r--src/lib/corelib/language/preparescriptobserver.cpp22
-rw-r--r--src/lib/corelib/language/preparescriptobserver.h11
-rw-r--r--src/lib/corelib/language/probesresolver.cpp60
-rw-r--r--src/lib/corelib/language/projectresolver.cpp41
-rw-r--r--src/lib/corelib/language/propertydeclaration.cpp1
-rw-r--r--src/lib/corelib/language/scriptengine.cpp813
-rw-r--r--src/lib/corelib/language/scriptengine.h249
-rw-r--r--src/lib/corelib/language/scriptimporter.cpp24
-rw-r--r--src/lib/corelib/language/scriptimporter.h9
-rw-r--r--src/lib/corelib/language/scriptpropertyobserver.h7
-rw-r--r--src/lib/corelib/tools/codelocation.cpp3
-rw-r--r--src/lib/corelib/tools/codelocation.h2
-rw-r--r--src/lib/corelib/tools/error.cpp19
-rw-r--r--src/lib/corelib/tools/progressobserver.h9
-rw-r--r--src/lib/corelib/tools/scripttools.cpp243
-rw-r--r--src/lib/corelib/tools/scripttools.h160
-rw-r--r--src/lib/corelib/tools/settingsrepresentation.cpp22
-rw-r--r--src/lib/corelib/tools/stringconstants.h1
-rw-r--r--src/lib/libs.qbs1
-rw-r--r--src/lib/scriptengine/CMakeLists.txt367
-rw-r--r--src/lib/scriptengine/scriptengine.qbs464
-rw-r--r--src/packages/archive/archive.qbs1
-rw-r--r--src/shared/CMakeLists.txt1
-rw-r--r--src/shared/bundledqt/bundledqt.qbs1
m---------src/shared/qtscript0
-rw-r--r--src/shared/quickjs/.clang-tidy13
-rw-r--r--src/shared/quickjs/CMakeLists.txt33
-rw-r--r--src/shared/quickjs/LICENSE22
-rw-r--r--src/shared/quickjs/cutils.c630
-rw-r--r--src/shared/quickjs/cutils.h309
-rw-r--r--src/shared/quickjs/libregexp-opcode.h58
-rw-r--r--src/shared/quickjs/libregexp.c2610
-rw-r--r--src/shared/quickjs/libregexp.h92
-rw-r--r--src/shared/quickjs/libunicode-table.h4449
-rw-r--r--src/shared/quickjs/libunicode.c1558
-rw-r--r--src/shared/quickjs/libunicode.h124
-rw-r--r--src/shared/quickjs/list.h100
-rw-r--r--src/shared/quickjs/quickjs-atom.h273
-rw-r--r--src/shared/quickjs/quickjs-opcode.h365
-rw-r--r--src/shared/quickjs/quickjs.c54315
-rw-r--r--src/shared/quickjs/quickjs.diff4061
-rw-r--r--src/shared/quickjs/quickjs.h1020
-rw-r--r--src/shared/quickjs/quickjs.qbs33
-rw-r--r--src/src.qbs3
-rw-r--r--tests/auto/api/tst_api.cpp4
-rw-r--r--tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs67
-rw-r--r--tests/auto/blackbox/testdata/concurrent-executor/dummy1.input0
-rw-r--r--tests/auto/blackbox/testdata/concurrent-executor/dummy2.input0
-rw-r--r--tests/auto/blackbox/testdata/concurrent-executor/util.js8
-rw-r--r--tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs3
-rw-r--r--tests/auto/blackbox/testdata/require-deprecated/blubb.js13
-rw-r--r--tests/auto/blackbox/testdata/require-deprecated/require.qbs21
-rw-r--r--tests/auto/blackbox/testdata/require-deprecated/zort.js11
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp26
-rw-r--r--tests/auto/blackbox/tst_blackbox.h2
-rw-r--r--tests/auto/blackbox/tst_blackboxbase.cpp2
-rw-r--r--tests/auto/buildgraph/CMakeLists.txt2
-rw-r--r--tests/auto/language/CMakeLists.txt3
-rw-r--r--tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/original-in-export-item.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/original-in-export-item2.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/original-in-export-item3.qbs2
-rw-r--r--tests/auto/language/tst_language.cpp51
-rw-r--r--tests/auto/pkgconfig/CMakeLists.txt2
149 files changed, 75512 insertions, 6077 deletions
diff --git a/.clang-tidy b/.clang-tidy
index 6fab186aa..e737372e9 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -3,8 +3,8 @@ Checks: >
-*,
bugprone-*,
-bugprone-narrowing-conversions,
+ -bugprone-throw-keyword-missing,
cppcoreguidelines-interfaces-global-init,
- cppcoreguidelines-pro-type-cstyle-cast,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-slicing,
fuchsia-virtual-inheritance,
diff --git a/.github/actions/download-qt/action.yml b/.github/actions/download-qt/action.yml
index e27358125..a89a33884 100644
--- a/.github/actions/download-qt/action.yml
+++ b/.github/actions/download-qt/action.yml
@@ -17,6 +17,6 @@ runs:
steps:
- name: Install Qt
run: |
- QT_DIR=$(./scripts/install-qt.sh -d $HOME/Qt --version ${{ inputs.version }} --target ${{ inputs.target }} --toolchain ${{ inputs.toolchain }} qtbase qtdeclarative qttools qtscript qtscxml qt5compat)
+ QT_DIR=$(./scripts/install-qt.sh -d $HOME/Qt --version ${{ inputs.version }} --target ${{ inputs.target }} --toolchain ${{ inputs.toolchain }} qtbase qtdeclarative qttools qtscxml qt5compat)
(cygpath -w ${QTC_DIR} 2>/dev/null || echo ${QT_DIR}) >> ${GITHUB_PATH}
shell: bash
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6c99c35df..27bd7a75d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -189,7 +189,7 @@ jobs:
mkdir release
mkdir tmp
- name: Copy sources
- run: rsync -av --exclude='.git/' --exclude='tmp/' --exclude='src/shared/qtscript/.git' . ./tmp/qbs-src-${{ steps.get-version-name.outputs.version-name }}
+ run: rsync -av --exclude='.git/' --exclude='tmp/' . ./tmp/qbs-src-${{ steps.get-version-name.outputs.version-name }}
- name: Zip Archive
run: |
cd tmp/
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index f2f854c17..000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "qtscript"]
- path = src/shared/qtscript
- url = ../../qt/qtscript.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 94dbe6f2c..41728a399 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,10 +54,6 @@ else()
if(NOT TARGET Qt6Core5Compat)
add_library(Qt6Core5Compat INTERFACE)
endif()
- if (NOT QBS_USE_BUNDLED_QT_SCRIPT AND Qt5Script_FOUND)
- add_library(qbsscriptengine INTERFACE)
- target_link_libraries(qbsscriptengine INTERFACE Qt5::Script)
- endif()
endif()
if (QBS_INSTALL_HTML_DOCS OR QBS_INSTALL_QCH_DOCS)
diff --git a/VERSION b/VERSION
index f9e8384bb..227cea215 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.24.1
+2.0.0
diff --git a/cmake/QbsBuildConfig.cmake b/cmake/QbsBuildConfig.cmake
index cc2db2fb3..f70a1c53c 100644
--- a/cmake/QbsBuildConfig.cmake
+++ b/cmake/QbsBuildConfig.cmake
@@ -2,7 +2,9 @@ option(WITH_TESTS "Build Tests" ON)
option(WITH_UNIT_TESTS "Build Unit Tests" OFF)
option(INSTALL_PUBLIC_HEADERS "Whether to install public headers" ON)
option(QBS_ENABLE_RPATH "Whether to enable RPATH" ON)
-option(QBS_USE_BUNDLED_QT_SCRIPT "Whether to use bundled QtScript module" OFF)
+
+include(CMakeDependentOption)
+cmake_dependent_option(QBS_QUICKJS_LEAK_CHECK "Whether to check for quickjs leaks at the end" ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
set(QBS_APP_INSTALL_DIR "bin" CACHE STRING "Relative install location for Qbs binaries.")
# default paths
diff --git a/doc/codeattributions.qdoc b/doc/codeattributions.qdoc
index cdd58b159..092f7794f 100644
--- a/doc/codeattributions.qdoc
+++ b/doc/codeattributions.qdoc
@@ -180,7 +180,7 @@ modification, are permitted provided that the following conditions are met:
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of biplist nor the names of its contributors may be
- used to endorse or promote products derived from this software without
+ 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"
@@ -239,3 +239,50 @@ Copyright (c) 2008 Roberto Raggi
// THE SOFTWARE.
\endcode
*/
+
+/*!
+
+\ingroup attributions-libs
+\ingroup attributions-qbs
+\page qbs-attribution-quickjs.html attribution
+\target quickjs
+
+\title QuickJS
+\brief MIT License
+
+The JavaScript engine used to evaluate all JavaScript code in \QBS project files.
+
+The sources can be found in src/shared/quickjs.
+
+\badcode
+Copyright (c) 2017-2021 Fabrice Bellard
+Copyright (c) 2017-2021 Charlie Gordon
+\endcode
+
+\l{https://spdx.org/licenses/MIT.html}{MIT License}.
+
+\badcode
+QuickJS Javascript Engine
+
+Copyright (c) 2017-2021 Fabrice Bellard
+Copyright (c) 2017-2021 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.
+\endcode
+*/
diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc
index 7fcc7e68b..6b5981674 100644
--- a/doc/qbs.qdoc
+++ b/doc/qbs.qdoc
@@ -556,7 +556,6 @@
\row \li qbs_no_dev_install \li Exclude header files from installation, that is, perform a
non-developer build.
\row \li qbs_no_man_install \li Exclude the man page from installation.
- \row \li qbs_use_bundled_qtscript \li Use the bundled QtScript library.
\endtable
In addition, you can set the \c QBS_SYSTEM_SETTINGS_DIR environment variable
@@ -643,11 +642,6 @@
\li Holds whether the Qt libraries that \QBS depends on will be bundled with \QBS during
the \c install step. This option is only implemented on macOS.
\row
- \li useBundledQtScript
- \li \c false
- \li Use the bundled QtScript module instead of the one shipped with Qt. In that case,
- QtScript should be checked out as a git submodule.
- \row
\li libDirName
\li \c "lib"
\li Directory name used by \c libInstallDir and \c importLibInstallDir properties.
diff --git a/docker/focal/Dockerfile b/docker/focal/Dockerfile
index 50354431f..bc6378912 100644
--- a/docker/focal/Dockerfile
+++ b/docker/focal/Dockerfile
@@ -85,7 +85,7 @@ ENV LLVM_INSTALL_DIR=/usr/lib/llvm-12
#
COPY scripts/install-qt.sh install-qt.sh
-RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qtscript qttools qtx11extras qtscxml qt5compat icu && \
+RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qttools qtx11extras qtscxml qt5compat icu && \
./install-qt.sh --version ${QTCREATOR_VERSION} qtcreator && \
echo "export PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:\${PATH}" > /etc/profile.d/qt.sh
diff --git a/docker/leap/Dockerfile b/docker/leap/Dockerfile
index 308f854ab..12f789e21 100644
--- a/docker/leap/Dockerfile
+++ b/docker/leap/Dockerfile
@@ -76,7 +76,7 @@ RUN zypper install -y \
#
COPY scripts/install-qt.sh install-qt.sh
-RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qtscript qttools qtx11extras qtscxml qt5compat icu && \
+RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qttools qtx11extras qtscxml qt5compat icu && \
./install-qt.sh --version ${QTCREATOR_VERSION} qtcreator && \
echo "export PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:\${PATH}" > /etc/profile.d/qt.sh
diff --git a/docker/windowsservercore/Dockerfile b/docker/windowsservercore/Dockerfile
index 50a1400ef..0909d81f2 100644
--- a/docker/windowsservercore/Dockerfile
+++ b/docker/windowsservercore/Dockerfile
@@ -42,10 +42,10 @@ RUN certutil -generateSSTFromWU roots.sst && \
ARG QT_VERSION
COPY scripts/install-qt.sh install-qt.sh
-RUN bash -c "./install-qt.sh -d /c/Qt --version ${QT_VERSION} --toolchain win64_msvc2019_64 qtbase qtdeclarative qttools qtscript"
+RUN bash -c "./install-qt.sh -d /c/Qt --version ${QT_VERSION} --toolchain win64_msvc2019_64 qtbase qtdeclarative qttools"
ENV QTDIR64=C:\\Qt\\${QT_VERSION}\\msvc2019_64
-RUN bash -c "./install-qt.sh -d /c/Qt --version ${QT_VERSION} --toolchain win32_msvc2019 qtbase qtdeclarative qttools qtscript"
+RUN bash -c "./install-qt.sh -d /c/Qt --version ${QT_VERSION} --toolchain win32_msvc2019 qtbase qtdeclarative qttools"
ENV QTDIR=C:\\Qt\\${QT_VERSION}\\msvc2019
RUN qbs setup-toolchains --detect && \
diff --git a/qbs-resources/imports/QbsUnittest.qbs b/qbs-resources/imports/QbsUnittest.qbs
index 5b54b9a51..c956d5480 100644
--- a/qbs-resources/imports/QbsUnittest.qbs
+++ b/qbs-resources/imports/QbsUnittest.qbs
@@ -6,16 +6,5 @@ QbsAutotest {
name: "Qt.core5compat";
condition: Utilities.versionCompare(Qt.core.version, "6.0.0") >= 0
}
- Depends {
- name: "Qt.script"
- condition: !qbsbuildconfig.useBundledQtScript
- required: false
- }
- Depends {
- name: "qbsscriptengine"
- condition: qbsbuildconfig.useBundledQtScript || !Qt.script.present
- }
- property stringList bundledQtScriptIncludes: qbsbuildconfig.useBundledQtScript
- || !Qt.script.present ? qbsscriptengine.includePaths : []
- cpp.includePaths: base.concat(bundledQtScriptIncludes)
+ Depends { name: "quickjs"; cpp.link: false }
}
diff --git a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
index 0c548bf6d..13e4c6985 100644
--- a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
+++ b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
@@ -14,7 +14,6 @@ Module {
property bool enableRPath: true
property bool installApiHeaders: true
property bool enableBundledQt: false
- property bool useBundledQtScript: false
property bool staticBuild: false
property string libDirName: "lib"
property string appInstallDir: "bin"
@@ -47,11 +46,12 @@ Module {
property string pluginsInstallDir: libDirName + "/qbs/plugins"
property string qmlTypeDescriptionsInstallDir: FileInfo.joinPaths(resourcesInstallDir,
"share/qbs/qml-type-descriptions")
+ property bool dumpJsLeaks: qbs.buildVariant === "debug"
Properties {
condition: project.withCode && qbs.toolchain.contains("gcc")
cpp.cxxFlags: {
- var flags = [];
+ var flags = ["-Wno-missing-field-initializers"];
if (enableAddressSanitizer)
flags.push("-fno-omit-frame-pointer");
function isClang() { return qbs.toolchain.contains("clang"); }
diff --git a/scripts/address-sanitizer-suppressions.txt b/scripts/address-sanitizer-suppressions.txt
index b31f34a05..c4731b213 100644
--- a/scripts/address-sanitizer-suppressions.txt
+++ b/scripts/address-sanitizer-suppressions.txt
@@ -1,4 +1,2 @@
-leak:libQt5Script.so.5
-leak:libqbsscriptengine.so
leak:QThreadPrivate::QThreadPrivate
leak:QArrayData::allocate
diff --git a/scripts/install-qt.sh b/scripts/install-qt.sh
index 417a708b0..f1162eb15 100755
--- a/scripts/install-qt.sh
+++ b/scripts/install-qt.sh
@@ -45,7 +45,7 @@ usage: install-qt [options] [components]
Examples
./install-qt.sh --version 5.13.1 qtbase
- ./install-qt.sh --version 5.14.0 --target android --toolchain any qtbase qtscript
+ ./install-qt.sh --version 5.14.0 --target android --toolchain any qtbase
Positional arguments
components
diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js
index 1b70904c0..748095aff 100644
--- a/share/qbs/modules/cpp/msvc.js
+++ b/share/qbs/modules/cpp/msvc.js
@@ -387,11 +387,11 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
var linkDLL = (outputs.dynamiclibrary ? true : false)
var primaryOutput = (linkDLL ? outputs.dynamiclibrary[0] : outputs.application[0])
var debugInformation = product.cpp.debugInformation;
- var additionalManifestInputs = Array.prototype.map.call(inputs["native.pe.manifest"],
+ var additionalManifestInputs = Array.prototype.map.call(inputs["native.pe.manifest"] || [],
function (a) {
return a.filePath;
});
- var moduleDefinitionInputs = Array.prototype.map.call(inputs["def"],
+ var moduleDefinitionInputs = Array.prototype.map.call(inputs["def"] || [],
function (a) {
return a.filePath;
});
diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js
index 928ded5cf..9861da296 100644
--- a/share/qbs/modules/cpp/sdcc.js
+++ b/share/qbs/modules/cpp/sdcc.js
@@ -461,10 +461,9 @@ function renameLinkerMapFile(project, product, inputs, outputs, input, output) {
// remove a listing files only after the linking completes.
function removeCompilerListingFiles(project, product, inputs, outputs, input, output) {
var cmd = new JavaScriptCommand();
- cmd.objects = inputs.obj.map(function(a) { return a; });
cmd.silent = true;
cmd.sourceCode = function() {
- objects.forEach(function(object) {
+ inputs.obj.forEach(function(object) {
if (!object.filePath.endsWith(".c" + object.cpp.objectSuffix))
return; // Skip the assembler generated objects.
if (!object.cpp.generateCompilerListingFiles
diff --git a/share/qbs/modules/cpp/windows-clang-cl.qbs b/share/qbs/modules/cpp/windows-clang-cl.qbs
index cc58097f6..bc69faf06 100644
--- a/share/qbs/modules/cpp/windows-clang-cl.qbs
+++ b/share/qbs/modules/cpp/windows-clang-cl.qbs
@@ -86,13 +86,14 @@ MsvcBaseModule {
linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName)
validateFunc: {
+ var baseFunc = base;
return function() {
if (_skipAllChecks)
return;
var validator = new ModUtils.PropertyValidator("cpp");
validator.setRequiredProperty("vcvarsallPath", vcvarsallPath);
validator.validate();
- base();
+ baseFunc();
}
}
}
diff --git a/share/qbs/modules/dmg/dmg.js b/share/qbs/modules/dmg/dmg.js
index 4d972db9b..636a34af3 100644
--- a/share/qbs/modules/dmg/dmg.js
+++ b/share/qbs/modules/dmg/dmg.js
@@ -57,7 +57,7 @@ function dmgbuildSettings(product, inputs) {
volumeIcon = volumeIcons[0].filePath;
}
- var licenseFileObjects = Array.prototype.map.call(inputs["dmg.license"], function (a) {
+ var licenseFileObjects = Array.prototype.map.call(inputs["dmg.license"] || [], function (a) {
return {
"dmg": {
"licenseLocale": localizationFromArtifact(a),
@@ -97,7 +97,7 @@ function dmgbuildSettings(product, inputs) {
}, {});
}
- var contentsArray = Array.prototype.map.call(inputs["dmg.input"], function (a) {
+ var contentsArray = Array.prototype.map.call(inputs["dmg.input"] || [], function (a) {
if (a.dmg.sourceBase && !a.filePath.startsWith(a.dmg.sourceBase)) {
throw new Error("Cannot install '" + a.filePath + "', " +
"because it doesn't start with the value of " +
@@ -114,7 +114,7 @@ function dmgbuildSettings(product, inputs) {
};
});
- Array.prototype.forEach.call(product.dmg.iconPositions, function (obj) {
+ Array.prototype.forEach.call(product.dmg.iconPositions || [], function (obj) {
var existingIndex = -1;
Array.prototype.forEach.call(contentsArray, function (contentsItem, i) {
if (contentsItem["name"] === obj["path"])
diff --git a/share/qbs/modules/qbs/common.qbs b/share/qbs/modules/qbs/common.qbs
index 8ddfc582d..530bcb2d2 100644
--- a/share/qbs/modules/qbs/common.qbs
+++ b/share/qbs/modules/qbs/common.qbs
@@ -143,8 +143,9 @@ Module {
validator.addCustomValidator("architecture", architecture, function (value) {
return !architecture || architecture === Utilities.canonicalArchitecture(architecture);
- }, "'" + architecture + "' is invalid. You must use the canonical name '" +
- Utilities.canonicalArchitecture(architecture) + "'");
+ }, "'" + architecture + "' is invalid." + (architecture
+ ? " You must use the canonical name '" + Utilities.canonicalArchitecture(architecture)
+ : "") + "'");
validator.addCustomValidator("toolchain", toolchain, function (value) {
if (toolchain === undefined)
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index 48eee2608..f06c3168e 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -1,7 +1,3 @@
-if (QBS_USE_BUNDLED_QT_SCRIPT OR NOT Qt5Script_FOUND)
- add_subdirectory(scriptengine)
-endif()
-
add_subdirectory(pkgconfig)
add_subdirectory(corelib)
add_subdirectory(msbuild)
diff --git a/src/lib/corelib/CMakeLists.txt b/src/lib/corelib/CMakeLists.txt
index 113013479..ff3be4071 100644
--- a/src/lib/corelib/CMakeLists.txt
+++ b/src/lib/corelib/CMakeLists.txt
@@ -110,7 +110,6 @@ set(BUILD_GRAPH_SOURCES
rulesapplicator.h
rulesevaluationcontext.cpp
rulesevaluationcontext.h
- scriptclasspropertyiterator.h
timestampsupdater.cpp
timestampsupdater.h
transformer.cpp
@@ -172,7 +171,6 @@ list_transform_prepend(JS_EXTENSIONS_SOURCES jsextensions/)
if(APPLE)
set(JS_EXTENSIONS_MACOS_SOURCES
- propertylist_darwin.h
propertylist_darwin.mm
propertylistutils.h
propertylistutils.mm
@@ -194,11 +192,8 @@ set(LANGUAGE_SOURCES
builtindeclarations.cpp
builtindeclarations.h
deprecationinfo.h
- evaluationdata.h
evaluator.cpp
evaluator.h
- evaluatorscriptclass.cpp
- evaluatorscriptclass.h
filecontext.cpp
filecontext.h
filecontextbase.cpp
@@ -435,7 +430,7 @@ add_qbs_library(qbscore
Qt${QT_VERSION_MAJOR}::Xml
Qt6Core5Compat
qbspkgconfig
- qbsscriptengine
+ qbsquickjs
PUBLIC_DEPENDS
Qt${QT_VERSION_MAJOR}::Core
${EXTERNAL_DEPENDS}
diff --git a/src/lib/corelib/api/internaljobs.cpp b/src/lib/corelib/api/internaljobs.cpp
index f3bb46d48..6c2987cd2 100644
--- a/src/lib/corelib/api/internaljobs.cpp
+++ b/src/lib/corelib/api/internaljobs.cpp
@@ -50,6 +50,7 @@
#include <buildgraph/rulesevaluationcontext.h>
#include <language/language.h>
#include <language/loader.h>
+#include <language/scriptengine.h>
#include <logging/logger.h>
#include <logging/translator.h>
#include <tools/buildgraphlocker.h>
@@ -76,6 +77,8 @@ public:
{
std::lock_guard<std::mutex> lock(m_cancelMutex);
m_canceled = true;
+ if (scriptEngine())
+ scriptEngine()->cancel();
}
private:
diff --git a/src/lib/corelib/api/projectfileupdater.cpp b/src/lib/corelib/api/projectfileupdater.cpp
index fa42352cc..b31cdf7a3 100644
--- a/src/lib/corelib/api/projectfileupdater.cpp
+++ b/src/lib/corelib/api/projectfileupdater.cpp
@@ -66,7 +66,7 @@ namespace Internal {
class ItemFinder : public Visitor
{
public:
- ItemFinder(const CodeLocation &cl) : m_cl(cl), m_item(nullptr) { }
+ ItemFinder(CodeLocation cl) : m_cl(std::move(cl)), m_item(nullptr) { }
UiObjectDefinition *item() const { return m_item; }
diff --git a/src/lib/corelib/buildgraph/artifact.cpp b/src/lib/corelib/buildgraph/artifact.cpp
index 8d3a8bd3f..8f94353e6 100644
--- a/src/lib/corelib/buildgraph/artifact.cpp
+++ b/src/lib/corelib/buildgraph/artifact.cpp
@@ -64,6 +64,8 @@ Artifact::~Artifact()
{
for (Artifact *p : parentArtifacts())
p->childrenAddedByScanner.remove(this);
+ if (m_deregister)
+ m_deregister(this);
}
void Artifact::accept(BuildGraphVisitor *visitor)
diff --git a/src/lib/corelib/buildgraph/artifact.h b/src/lib/corelib/buildgraph/artifact.h
index 2572a3b52..fce34b6ef 100644
--- a/src/lib/corelib/buildgraph/artifact.h
+++ b/src/lib/corelib/buildgraph/artifact.h
@@ -50,6 +50,7 @@
#include <QtCore/qstring.h>
+#include <functional>
#include <utility>
#include <vector>
@@ -123,8 +124,12 @@ public:
void load(PersistentPool &pool) override;
void store(PersistentPool &pool) override;
+ using Deregister = std::function<void(const Artifact *)>;
+ void setDeregister(const Deregister &deregister) { m_deregister = deregister; }
+
private:
FileTags m_fileTags;
+ Deregister m_deregister;
};
template<> inline QString Set<Artifact *>::toString(Artifact * const &artifact) const
diff --git a/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp b/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp
index 3f3fce59b..6a470bc17 100644
--- a/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp
+++ b/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp
@@ -46,172 +46,177 @@
#include <language/scriptengine.h>
#include <tools/stlutils.h>
-
-#include <QtScript/qscriptclass.h>
-#include <QtScript/qscriptcontext.h>
+#include <tools/stringconstants.h>
namespace qbs {
namespace Internal {
-enum BuildGraphScriptValueCommonPropertyKeys : quint32 {
- CachedValueKey,
- FileTagKey,
- ProductPtrKey,
-};
-
-class ArtifactsScriptClass : public QScriptClass
+template<class ProductOrModule>
+static bool isRelevantArtifact(const ProductOrModule *productOrModule, const Artifact *artifact)
{
-public:
- ArtifactsScriptClass(QScriptEngine *engine) : QScriptClass(engine) { }
-
-private:
- QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name,
- QueryFlags flags, uint *id) override
- {
- getProduct(object);
- qbsEngine()->setNonExistingArtifactSetRequested(m_product, name.toString());
- return QScriptClass::queryProperty(object, name, flags, id);
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) {
+ Q_UNUSED(productOrModule)
+ return !artifact->isTargetOfModule();
+ } else {
+ return artifact->targetOfModule == productOrModule->name;
}
-
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override
- {
- getProduct(object);
- qbsEngine()->setArtifactsEnumerated(m_product);
- return QScriptClass::newIterator(object);
- }
-
- void getProduct(const QScriptValue &object)
- {
- if (m_lastObjectId != object.objectId()) {
- m_lastObjectId = object.objectId();
- m_product = reinterpret_cast<const ResolvedProduct *>(
- object.data().property(ProductPtrKey).toVariant().value<quintptr>());
- }
- }
-
- ScriptEngine *qbsEngine() const { return static_cast<ScriptEngine *>(engine()); }
-
- qint64 m_lastObjectId = 0;
- const ResolvedProduct *m_product = nullptr;
-};
-
-static bool isRelevantArtifact(const ResolvedProduct *, const Artifact *artifact)
-{
- return !artifact->isTargetOfModule();
-}
-static bool isRelevantArtifact(const ResolvedModule *module, const Artifact *artifact)
-{
- return artifact->targetOfModule == module->name;
}
-static ArtifactSetByFileTag artifactsMap(const ResolvedProduct *product)
+template<class ProductOrModule>
+static ArtifactSetByFileTag artifactsMap(const ProductOrModule *productOrModule)
{
- return product->buildData->artifactsByFileTag();
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ return productOrModule->buildData->artifactsByFileTag();
+ else
+ return artifactsMap(productOrModule->product);
}
-static ArtifactSetByFileTag artifactsMap(const ResolvedModule *module)
+template<class ProductOrModule> static int scriptClassIndex()
{
- return artifactsMap(module->product);
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ return 0;
+ return 1;
}
-static QScriptValue createArtifactsObject(const ResolvedProduct *product, ScriptEngine *engine)
+template<class ProductOrModule>
+std::unique_lock<std::mutex> getArtifactsMapLock(ProductOrModule *productOrModule)
{
- QScriptClass *scriptClass = engine->artifactsScriptClass();
- if (!scriptClass) {
- scriptClass = new ArtifactsScriptClass(engine);
- engine->setArtifactsScriptClass(scriptClass);
- }
- QScriptValue artifactsObj = engine->newObject(scriptClass);
- QScriptValue data = engine->newObject();
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(product));
- data.setProperty(ProductPtrKey, engine->newVariant(v));
- artifactsObj.setData(data);
- return artifactsObj;
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ return productOrModule->buildData->getArtifactsMapLock();
+ else
+ return getArtifactsMapLock(productOrModule->product);
}
-static QScriptValue createArtifactsObject(const ResolvedModule *, ScriptEngine *engine)
+template<class ProductOrModule>
+static bool checkAndSetArtifactsMapUpToDateFlag(const ProductOrModule *productOrModule)
{
- return engine->newObject();
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ return productOrModule->buildData->checkAndSetJsArtifactsMapUpToDateFlag();
+ else
+ return checkAndSetArtifactsMapUpToDateFlag(productOrModule->product);
}
-static bool checkAndSetArtifactsMapUpToDateFlag(const ResolvedProduct *p)
+// Must be called with artifacts map lock held!
+template<class ProductOrModule>
+void registerArtifactsMapAccess(ScriptEngine *engine, ProductOrModule *productOrModule)
{
- return p->buildData->checkAndSetJsArtifactsMapUpToDateFlag();
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) {
+ if (!checkAndSetArtifactsMapUpToDateFlag(productOrModule))
+ engine->setArtifactsMapRequested(productOrModule, true);
+ else
+ engine->setArtifactsMapRequested(productOrModule, false);
+ } else {
+ registerArtifactsMapAccess(engine, productOrModule->product);
+ }
}
-static bool checkAndSetArtifactsMapUpToDateFlag(const ResolvedModule *) { return true; }
-static void registerArtifactsMapAccess(const ResolvedProduct *p, ScriptEngine *e, bool forceUpdate)
-{
- e->setArtifactsMapRequested(p, forceUpdate);
-}
-static void registerArtifactsMapAccess(const ResolvedModule *, ScriptEngine *, bool) {}
-static void registerArtifactsSetAccess(const ResolvedProduct *p, const FileTag &t, ScriptEngine *e)
+template<class ProductOrModule>
+static int getArtifactsPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj)
{
- e->setArtifactSetRequestedForTag(p, t);
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto productOrModule = attachedPointer<ProductOrModule>(
+ obj, engine->artifactsScriptClass(scriptClassIndex<ProductOrModule>()));
+ const std::unique_lock lock = getArtifactsMapLock(productOrModule);
+ registerArtifactsMapAccess(engine, productOrModule);
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ engine->setArtifactsEnumerated(productOrModule);
+ const auto &map = artifactsMap(productOrModule);
+ const auto filter = [productOrModule](const Artifact *a) {
+ return isRelevantArtifact(productOrModule, a);
+ };
+ QStringList tags;
+ for (auto it = map.cbegin(); it != map.cend(); ++it) {
+ if (any_of(it.value(), filter)) {
+ tags << it.key().toString();
+ }
+ }
+ *plen = tags.size();
+ if (!tags.isEmpty()) {
+ *ptab = reinterpret_cast<JSPropertyEnum *>(js_malloc(ctx, *plen * sizeof **ptab));
+ JSPropertyEnum *entry = *ptab;
+ for (const QString &tag : qAsConst(tags)) {
+ entry->atom = JS_NewAtom(ctx, tag.toUtf8().constData());
+ entry->is_enumerable = 1;
+ ++entry;
+ }
+ } else {
+ *ptab = nullptr;
+ }
+ return 0;
}
-static void registerArtifactsSetAccess(const ResolvedModule *, const FileTag &, ScriptEngine *) {}
-template<class ProductOrModule> static QScriptValue js_artifactsForFileTag(
- QScriptContext *ctx, ScriptEngine *engine, const ProductOrModule *productOrModule)
+template<class ProductOrModule>
+static int getArtifactsProperty(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop)
{
- const FileTag fileTag = FileTag(ctx->callee().property(FileTagKey).toString().toUtf8());
- registerArtifactsSetAccess(productOrModule, fileTag, engine);
- QScriptValue result = ctx->callee().property(CachedValueKey);
- if (result.isArray())
- return result;
- auto artifacts = artifactsMap(productOrModule).value(fileTag);
- const auto filter = [productOrModule](const Artifact *a) {
+ if (!desc)
+ return 1;
+
+ desc->flags = JS_PROP_ENUMERABLE;
+ desc->value = desc->getter = desc->setter = JS_UNDEFINED;
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto productOrModule = attachedPointer<ProductOrModule>(
+ obj, engine->artifactsScriptClass(scriptClassIndex<ProductOrModule>()));
+ const std::unique_lock lock = getArtifactsMapLock(productOrModule);
+ registerArtifactsMapAccess(engine, productOrModule);
+ const QString tagString = getJsString(ctx, prop);
+ const FileTag fileTag(tagString.toUtf8());
+ const auto &map = artifactsMap(productOrModule);
+ const auto it = map.constFind(fileTag);
+ if (it == map.constEnd()) {
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ engine->setNonExistingArtifactSetRequested(productOrModule, tagString);
+ return 1;
+ }
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ engine->setArtifactSetRequestedForTag(productOrModule, fileTag);
+ ArtifactSet artifacts = it.value();
+ removeIf(artifacts, [productOrModule](const Artifact *a) {
return !isRelevantArtifact(productOrModule, a);
- };
- Internal::removeIf(artifacts, filter);
- result = engine->newArray(uint(artifacts.size()));
- ctx->callee().setProperty(CachedValueKey, result);
- int k = 0;
- for (const Artifact * const artifact : artifacts)
- result.setProperty(k++, Transformer::translateFileConfig(engine, artifact, QString()));
- return result;
+ });
+ if (!artifacts.empty()) {
+ desc->value = JS_NewArray(ctx); // TODO: Also cache this list?
+ int k = 0;
+ for (Artifact * const artifact : artifacts) {
+ JS_SetPropertyUint32(ctx, desc->value, k++,
+ Transformer::translateFileConfig(engine, artifact, QString()));
+ }
+ }
+ return 1;
}
-template<class ProductOrModule> static QScriptValue js_artifacts(
- QScriptContext *ctx, ScriptEngine *engine, const ProductOrModule *productOrModule)
+template<class ProductOrModule> static JSValue js_artifacts(JSContext *ctx,
+ JSValue jsProductOrModule)
{
- QScriptValue artifactsObj = ctx->callee().property(CachedValueKey);
- if (artifactsObj.isObject() && checkAndSetArtifactsMapUpToDateFlag(productOrModule)) {
- registerArtifactsMapAccess(productOrModule, engine, false);
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto productOrModule = attachedPointer<ProductOrModule>(jsProductOrModule,
+ engine->dataWithPtrClass());
+ JSValue artifactsObj = engine->artifactsMapScriptValue(productOrModule);
+ if (!JS_IsUndefined(artifactsObj))
return artifactsObj;
+ const int classIndex = scriptClassIndex<ProductOrModule>();
+ JSClassID scriptClass = engine->artifactsScriptClass(classIndex);
+ if (scriptClass == 0) {
+ const QByteArray className = "ArtifactsScriptClass" + QByteArray::number(classIndex);
+ scriptClass = engine->registerClass(className.constData(), nullptr, nullptr, JS_UNDEFINED,
+ &getArtifactsPropertyNames<ProductOrModule>,
+ &getArtifactsProperty<ProductOrModule>);
+ engine->setArtifactsScriptClass(classIndex, scriptClass);
}
- registerArtifactsMapAccess(productOrModule, engine, true);
- artifactsObj = createArtifactsObject(productOrModule, engine);
- ctx->callee().setProperty(CachedValueKey, artifactsObj);
- const auto &map = artifactsMap(productOrModule);
- for (auto it = map.cbegin(); it != map.cend(); ++it) {
- const auto filter = [productOrModule](const Artifact *a) {
- return isRelevantArtifact(productOrModule, a);
- };
- if (std::none_of(it.value().cbegin(), it.value().cend(), filter))
- continue;
- QScriptValue fileTagFunc = engine->newFunction(&js_artifactsForFileTag<ProductOrModule>,
- productOrModule);
- const QString fileTag = it.key().toString();
- fileTagFunc.setProperty(FileTagKey, fileTag);
- artifactsObj.setProperty(fileTag, fileTagFunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
- }
+ artifactsObj = JS_NewObjectClass(engine->context(), scriptClass);
+ attachPointerTo(artifactsObj, productOrModule);
return artifactsObj;
}
-QScriptValue artifactsScriptValueForProduct(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedProduct *product)
+JSValue artifactsScriptValueForProduct(JSContext *ctx, JSValue this_val, int, JSValue *)
{
- return js_artifacts(ctx, engine, product);
+ return js_artifacts<ResolvedProduct>(ctx, this_val);
}
-QScriptValue artifactsScriptValueForModule(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedModule *module)
+JSValue artifactsScriptValueForModule(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- return js_artifacts(ctx, engine, module);
+ return js_artifacts<ResolvedModule>(ctx, this_val);
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/artifactsscriptvalue.h b/src/lib/corelib/buildgraph/artifactsscriptvalue.h
index c0da8a05e..dce457758 100644
--- a/src/lib/corelib/buildgraph/artifactsscriptvalue.h
+++ b/src/lib/corelib/buildgraph/artifactsscriptvalue.h
@@ -41,21 +41,14 @@
#include <language/forward_decls.h>
-#include <QtScript/qscriptvalue.h>
-
-QT_BEGIN_NAMESPACE
-class QScriptContext;
-QT_END_NAMESPACE
+#include <quickjs.h>
namespace qbs {
namespace Internal {
class ScriptEngine;
-QScriptValue artifactsScriptValueForProduct(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedProduct *product);
-
-QScriptValue artifactsScriptValueForModule(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedModule *module);
+JSValue artifactsScriptValueForProduct(JSContext *ctx, JSValueConst this_val, int, JSValueConst *);
+JSValue artifactsScriptValueForModule(JSContext *ctx, JSValueConst this_val, int, JSValueConst *);
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp
index 23d141dd8..df3301d57 100644
--- a/src/lib/corelib/buildgraph/buildgraph.cpp
+++ b/src/lib/corelib/buildgraph/buildgraph.cpp
@@ -45,7 +45,6 @@
#include "projectbuilddata.h"
#include "productbuilddata.h"
#include "rulenode.h"
-#include "scriptclasspropertyiterator.h"
#include "transformer.h"
#include <jsextensions/jsextensions.h>
@@ -70,7 +69,6 @@
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
-#include <QtScript/qscriptclass.h>
#include <algorithm>
#include <iterator>
@@ -83,146 +81,187 @@ static QString childItemsProperty() { return QStringLiteral("childItems"); }
static QString exportsProperty() { return QStringLiteral("exports"); }
// TODO: Introduce productscriptvalue.{h,cpp}.
-static QScriptValue getDataForProductScriptValue(QScriptEngine *engine,
- const ResolvedProduct *product)
+
+static JSValue getDataObject(JSContext *ctx, JSValue obj)
{
- QScriptValue data = engine->newObject();
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(product));
- data.setProperty(ProductPtrKey, engine->newVariant(v));
- return data;
+ return getJsProperty(ctx, obj, StringConstants::dataPropertyInternal());
}
-class ProductPropertyScriptClass : public QScriptClass
+static JSValue getValueFromData(JSContext *ctx, JSValue data,
+ ModulePropertiesScriptValueCommonPropertyKeys key)
{
-public:
- ProductPropertyScriptClass(QScriptEngine *engine) : QScriptClass(engine) { }
-
-private:
- QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags,
- uint *) override
- {
- if (name == StringConstants::parametersProperty()) {
- m_result = object.data().property(DependencyParametersKey);
- return HandlesReadAccess;
- }
- if (name == StringConstants::moduleNameProperty()) {
- m_result = object.data().property(ModuleNameKey);
- return HandlesReadAccess;
- }
- if (name == StringConstants::dependenciesProperty()
- || name == StringConstants::artifactsProperty()
- || name == exportsProperty()) {
- // The prototype is not backed by a QScriptClass.
- m_result = object.prototype().property(name);
- return HandlesReadAccess;
- }
-
- getProduct(object);
- QBS_ASSERT(m_product, return {});
-
- const auto it = m_product->productProperties.find(name);
+ return JS_GetPropertyUint32(ctx, data, key);
+}
- // It is important that we reject unknown property names. Otherwise QtScript will forward
- // *everything* to us, including built-in stuff like the hasOwnProperty function.
- if (it == m_product->productProperties.cend())
- return {};
+static JSValue getValueFromObject(JSContext *ctx, JSValue obj,
+ ModulePropertiesScriptValueCommonPropertyKeys key)
+{
+ const ScopedJsValue data(ctx, getDataObject(ctx, obj));
+ return getValueFromData(ctx, data, key);
+}
- qbsEngine()->addPropertyRequestedInScript(Property(m_product->uniqueName(), QString(), name,
- it.value(), Property::PropertyInProduct));
- m_result = qbsEngine()->toScriptValue(it.value());
- return HandlesReadAccess;
+void getPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ const QVariantMap &properties, const QStringList &extraPropertyNames,
+ JSValue extraObject)
+{
+ JSPropertyEnum *basePTab = nullptr;
+ uint32_t basePlen = 0;
+ JS_GetOwnPropertyNames(ctx, &basePTab, &basePlen, extraObject,
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY);
+ *plen = uint32_t(extraPropertyNames.size() + properties.size() + basePlen);
+ *ptab = reinterpret_cast<JSPropertyEnum *>(js_malloc(ctx, *plen * sizeof **ptab));
+ JSPropertyEnum *entry = *ptab;
+ for (auto it = properties.begin(); it != properties.end(); ++it, ++entry) {
+ entry->atom = JS_NewAtom(ctx, it.key().toUtf8().constData());
+ entry->is_enumerable = 1;
}
-
- QScriptValue property(const QScriptValue &, const QScriptString &, uint) override
- {
- return m_result;
+ for (const QString &prop : extraPropertyNames) {
+ entry->atom = JS_NewAtom(ctx, prop.toUtf8().constData());
+ entry->is_enumerable = 1;
+ ++entry;
+ }
+ for (uint32_t i = 0; i < basePlen; ++i, ++entry) {
+ entry->atom = basePTab[i].atom;
+ entry->is_enumerable = 1;
+ }
+ js_free(ctx, basePTab);
+}
+
+static int getProductPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj)
+{
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const ScopedJsValue data(ctx, getDataObject(engine->context(), obj));
+ const auto product = attachedPointer<ResolvedProduct>(
+ obj, engine->productPropertyScriptClass());
+ QBS_ASSERT(product, return -1);
+
+ // The "moduleName" convenience property is only available for the "main product" in a rule,
+ // and the "parameters" property exists only for elements of the "dependencies" array for
+ // which dependency parameters are present.
+ const auto hasModuleName = [&] {
+ const ScopedJsValue v(ctx, getValueFromData(ctx, data, ModuleNameKey));
+ return JS_IsString(v);
+ };
+ const auto hasDependencyParams = [&] {
+ const ScopedJsValue v(ctx, getValueFromData(ctx, data, DependencyParametersKey));
+ return JS_IsObject(v);
+ };
+ QStringList additionalProperties;
+ if (hasModuleName())
+ additionalProperties.push_back(StringConstants::moduleNameProperty());
+ else if (hasDependencyParams())
+ additionalProperties.push_back(StringConstants::parametersProperty());
+ getPropertyNames(ctx, ptab, plen, product->productProperties, additionalProperties,
+ engine->baseProductScriptValue(product));
+ return 0;
+}
+
+static int getProductProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj,
+ JSAtom prop)
+{
+ if (desc) {
+ desc->getter = desc->setter = desc->value = JS_UNDEFINED;
+ desc->flags = JS_PROP_ENUMERABLE;
+ }
+ const QString name = getJsString(ctx, prop);
+ if (name == StringConstants::parametersProperty()) {
+ if (desc)
+ desc->value = getValueFromObject(ctx, obj, DependencyParametersKey);
+ return 1;
+ }
+ if (name == StringConstants::moduleNameProperty()) {
+ if (desc)
+ desc->value = getValueFromObject(ctx, obj, ModuleNameKey);
+ return 1;
}
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override
- {
- getProduct(object);
- QBS_ASSERT(m_product, return nullptr);
-
- // These two are in the prototype and are thus common to all product script values.
- std::vector<QString> additionalProperties({StringConstants::artifactsProperty(),
- StringConstants::dependenciesProperty(),
- exportsProperty()});
-
- // The "moduleName" convenience property is only available for the "main product" in a rule,
- // and the "parameters" property exists only for elements of the "dependencies" array for
- // which dependency parameters are present.
- if (object.data().property(ModuleNameKey).isValid())
- additionalProperties.push_back(StringConstants::moduleNameProperty());
- else if (object.data().property(DependencyParametersKey).isValid())
- additionalProperties.push_back(StringConstants::parametersProperty());
- return new ScriptClassPropertyIterator(object, m_product->productProperties,
- additionalProperties);
- }
-
- void getProduct(const QScriptValue &object)
- {
- if (m_lastObjectId != object.objectId()) {
- m_lastObjectId = object.objectId();
- m_product = reinterpret_cast<const ResolvedProduct *>(
- object.data().property(ProductPtrKey).toVariant().value<quintptr>());
- }
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto product = attachedPointer<ResolvedProduct>(
+ obj, engine->productPropertyScriptClass());
+ QBS_ASSERT(product, return -1);
+
+ const JSValue baseProductValue = engine->baseProductScriptValue(product);
+ if (name == exportsProperty()) {
+ if (desc)
+ desc->value = getJsProperty(ctx, baseProductValue, name);
+ engine->addRequestedExport(product);
+ return 1;
}
- ScriptEngine *qbsEngine() const { return static_cast<ScriptEngine *>(engine()); }
+ const auto it = product->productProperties.constFind(name);
+ if (it == product->productProperties.cend()) {
+ ScopedJsValue v(ctx, JS_GetProperty(ctx, baseProductValue, prop));
+ const int ret = JS_IsUndefined(v) ? 0 : 1;
+ if (desc)
+ desc->value = v.release();
+ return ret;
+ }
- qint64 m_lastObjectId = 0;
- const ResolvedProduct *m_product = nullptr;
- QScriptValue m_result;
-};
+ engine->addPropertyRequestedInScript(Property(product->uniqueName(), QString(), name,
+ it.value(), Property::PropertyInProduct));
+ if (desc)
+ desc->value = engine->toScriptValue(it.value());
+ return 1;
+}
-static QScriptValue setupProjectScriptValue(ScriptEngine *engine,
- const ResolvedProjectConstPtr &project)
+static JSValue setupProjectScriptValue(ScriptEngine *engine, const ResolvedProjectConstPtr &project)
{
- QScriptValue &obj = engine->projectScriptValue(project.get());
- if (obj.isValid())
- return obj;
+ JSValue &obj = engine->projectScriptValue(project.get());
+ if (JS_IsObject(obj))
+ return JS_DupValue(engine->context(), obj);
obj = engine->newObject();
- obj.setProperty(StringConstants::filePathProperty(), project->location.filePath());
- obj.setProperty(StringConstants::pathProperty(), FileInfo::path(project->location.filePath()));
+ setJsProperty(engine->context(), obj, StringConstants::filePathProperty(),
+ project->location.filePath());
+ setJsProperty(engine->context(), obj, StringConstants::pathProperty(),
+ FileInfo::path(project->location.filePath()));
const QVariantMap &projectProperties = project->projectProperties();
for (QVariantMap::const_iterator it = projectProperties.begin();
it != projectProperties.end(); ++it) {
- engine->setObservedProperty(obj, it.key(), engine->toScriptValue(it.value()));
+ const ScopedJsValue val(engine->context(), engine->toScriptValue(it.value()));
+ engine->setObservedProperty(obj, it.key(), val);
}
- engine->observer()->addProjectObjectId(obj.objectId(), project->name);
- return obj;
+ engine->observer()->addProjectObjectId(jsObjectId(obj), project->name);
+ return JS_DupValue(engine->context(), obj);
}
-static QScriptValue setupProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product);
+static void setupBaseProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product);
class DependenciesFunction
{
public:
- DependenciesFunction(ScriptEngine *engine)
- : m_engine(engine)
- {
- }
+ DependenciesFunction(ScriptEngine *engine) : m_engine(engine) { }
- void init(QScriptValue &productScriptValue, QScriptValue &exportsScriptValue,
- const ResolvedProduct *product)
+ void init(JSValue &productScriptValue, JSValue exportsScriptValue)
{
- QScriptValue depfunc = m_engine->newFunction(&js_internalProductDependencies, product);
- productScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
- depfunc = m_engine->newFunction(&js_exportedProductDependencies, product);
- exportsScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
+ const QByteArray name = StringConstants::dependenciesProperty().toUtf8();
+ const ScopedJsValue depfunc(
+ m_engine->context(),
+ JS_NewCFunction(m_engine->context(), &js_internalProductDependencies,
+ name.constData(), 0));
+ const ScopedJsAtom depAtom(m_engine->context(), name);
+ JS_DefineProperty(m_engine->context(), productScriptValue, depAtom, JS_UNDEFINED, depfunc,
+ JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
+ const ScopedJsValue exportedDepfunc(
+ m_engine->context(),
+ JS_NewCFunction(m_engine->context(), &js_exportedProductDependencies,
+ name.constData(), 0));
+ JS_DefineProperty(m_engine->context(), exportsScriptValue, depAtom, JS_UNDEFINED,
+ exportedDepfunc, JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
}
private:
enum class DependencyType { Internal, Exported };
- static QScriptValue js_productDependencies(QScriptContext *, ScriptEngine *engine,
- const ResolvedProduct *product, DependencyType depType)
+ static JSValue js_productDependencies(const ResolvedProduct *product,
+ JSContext *ctx, JSValueConst this_val, int argc,
+ JSValueConst *argv, DependencyType depType)
{
- QScriptValue result = engine->newArray();
+ Q_UNUSED(this_val)
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ JSValue result = JS_NewArray(ctx);
quint32 idx = 0;
const bool exportCase = depType == DependencyType::Exported;
std::vector<ResolvedProductPtr> productDeps;
@@ -245,201 +284,233 @@ private:
productDeps = product->dependencies;
}
for (const ResolvedProductPtr &dependency : qAsConst(productDeps)) {
- QScriptValue obj = engine->newObject(engine->productPropertyScriptClass());
- obj.setPrototype(setupProductScriptValue(static_cast<ScriptEngine *>(engine),
- dependency.get()));
+ setupBaseProductScriptValue(engine, dependency.get());
+ JSValue obj = JS_NewObjectClass(engine->context(),
+ engine->productPropertyScriptClass());
+ attachPointerTo(obj, dependency.get());
const QVariantMap &params
= (exportCase ? product->exportedModule.dependencyParameters.value(dependency)
: product->dependencyParameters.value(dependency));
- QScriptValue data = getDataForProductScriptValue(engine, dependency.get());
- data.setProperty(DependencyParametersKey, dependencyParametersValue(
- product->uniqueName(), dependency->name, params, engine));
- obj.setData(data);
- result.setProperty(idx++, obj);
+ JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ JS_SetPropertyUint32(ctx, data, DependencyParametersKey, dependencyParametersValue(
+ product->uniqueName(), dependency->name, params, engine));
+ defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
}
if (exportCase) {
for (const ExportedModuleDependency &m : product->exportedModule.moduleDependencies) {
- QScriptValue obj = engine->newObject();
- obj.setProperty(StringConstants::nameProperty(), m.name);
- QScriptValue exportsValue = engine->newObject();
- obj.setProperty(exportsProperty(), exportsValue);
- exportsValue.setProperty(StringConstants::dependenciesProperty(),
- engine->newArray());
+ JSValue obj = engine->newObject();
+ setJsProperty(ctx, obj, StringConstants::nameProperty(), m.name);
+ JSValue exportsValue = engine->newObject();
+ setJsProperty(ctx, obj, exportsProperty(), exportsValue);
+ setJsProperty(ctx, exportsValue, StringConstants::dependenciesProperty(),
+ JS_NewArray(ctx));
for (auto modIt = m.moduleProperties.begin(); modIt != m.moduleProperties.end();
++modIt) {
const QVariantMap entries = modIt.value().toMap();
if (entries.empty())
continue;
- QScriptValue moduleObj = engine->newObject();
- ModuleProperties::setModuleScriptValue(exportsValue, moduleObj, modIt.key());
- for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt)
- moduleObj.setProperty(valIt.key(), engine->toScriptValue(valIt.value()));
+ JSValue moduleObj = engine->newObject();
+ ModuleProperties::setModuleScriptValue(engine, exportsValue, moduleObj,
+ modIt.key());
+ for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt) {
+ setJsProperty(ctx, moduleObj, valIt.key(),
+ engine->toScriptValue(valIt.value()));
+ }
}
- result.setProperty(idx++, obj);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
}
return result;
}
for (const auto &dependency : product->modules) {
if (dependency->isProduct)
continue;
- QScriptValue obj = engine->newObject(engine->modulePropertyScriptClass());
- obj.setPrototype(engine->moduleScriptValuePrototype(dependency.get()));
-
- // The prototype must exist already, because we set it up for all modules
- // of the product in ModuleProperties::init().
- QBS_ASSERT(obj.prototype().isValid(), ;);
-
+ JSValue obj = JS_NewObjectClass(ctx, engine->modulePropertyScriptClass());
+ attachPointerTo(obj, dependency.get());
const QVariantMap &params = product->moduleParameters.value(dependency);
- QScriptValue data = getDataForModuleScriptValue(engine, product, nullptr,
- dependency.get());
- data.setProperty(DependencyParametersKey, dependencyParametersValue(
- product->uniqueName(), dependency->name, params, engine));
- obj.setData(data);
- result.setProperty(idx++, obj);
+ JSValue data = createDataForModuleScriptValue(engine, nullptr);
+ JS_SetPropertyUint32(ctx, data, DependencyParametersKey, dependencyParametersValue(
+ product->uniqueName(), dependency->name, params, engine));
+ defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
}
return result;
}
- static QScriptValue js_internalProductDependencies(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedProduct * const product)
+ static JSValue js_internalProductDependencies(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto product = attachedPointer<ResolvedProduct>(this_val, engine->dataWithPtrClass());
engine->addDependenciesArrayRequested(product);
- return js_productDependencies(ctx, engine, product, DependencyType::Internal);
+ return js_productDependencies(product, ctx, this_val, argc, argv,
+ DependencyType::Internal);
}
- static QScriptValue js_exportedProductDependencies(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedProduct * const product)
+ static JSValue js_exportedProductDependencies(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- return js_productDependencies(ctx, engine, product, DependencyType::Exported);
+ const auto product = attachedPointer<ResolvedProduct>(
+ this_val, ScriptEngine::engineForContext(ctx)->dataWithPtrClass());
+ return js_productDependencies(product, ctx, this_val, argc, argv, DependencyType::Exported);
}
ScriptEngine *m_engine;
};
-static QScriptValue setupExportedPropertyScriptValue(const ExportedProperty &property,
+static JSValue setupExportedPropertyScriptValue(const ExportedProperty &property,
ScriptEngine *engine)
{
- QScriptValue propertyScriptValue = engine->newObject();
- propertyScriptValue.setProperty(StringConstants::nameProperty(), property.fullName);
- propertyScriptValue.setProperty(StringConstants::typeProperty(),
- PropertyDeclaration::typeString(property.type));
- propertyScriptValue.setProperty(StringConstants::sourceCodeProperty(), property.sourceCode);
- propertyScriptValue.setProperty(QStringLiteral("isBuiltin"), property.isBuiltin);
+ JSValue propertyScriptValue = engine->newObject();
+ setJsProperty(engine->context(), propertyScriptValue, StringConstants::nameProperty(),
+ property.fullName);
+ setJsProperty(engine->context(), propertyScriptValue, StringConstants::typeProperty(),
+ PropertyDeclaration::typeString(property.type));
+ setJsProperty(engine->context(), propertyScriptValue, StringConstants::sourceCodeProperty(),
+ property.sourceCode);
+ JS_SetPropertyStr(engine->context(), propertyScriptValue, "isBuiltin",
+ JS_NewBool(engine->context(), property.isBuiltin));
return propertyScriptValue;
}
-static void setupExportedPropertiesScriptValue(QScriptValue &parentObject,
+static void setupExportedPropertiesScriptValue(JSValue &parentObject,
const std::vector<ExportedProperty> &properties,
ScriptEngine *engine)
{
- QScriptValue propertiesScriptValue = engine->newArray(static_cast<uint>(properties.size()));
- parentObject.setProperty(QStringLiteral("properties"), propertiesScriptValue);
+ JSValue propertiesScriptValue = JS_NewArray(engine->context());
quint32 arrayIndex = 0;
for (const ExportedProperty &p : properties) {
- propertiesScriptValue.setProperty(arrayIndex++,
- setupExportedPropertyScriptValue(p, engine));
+ JS_SetPropertyUint32(engine->context(), propertiesScriptValue, arrayIndex++,
+ setupExportedPropertyScriptValue(p, engine));
}
+ JS_SetPropertyStr(engine->context(), parentObject, "properties", propertiesScriptValue);
}
-static QScriptValue setupExportedItemScriptValue(const ExportedItem *item, ScriptEngine *engine)
+static JSValue setupExportedItemScriptValue(const ExportedItem *item, ScriptEngine *engine)
{
- QScriptValue itemScriptValue = engine->newObject();
- itemScriptValue.setProperty(StringConstants::nameProperty(), item->name);
+ JSValue itemScriptValue = engine->newObject();
+ setJsProperty(engine->context(), itemScriptValue, StringConstants::nameProperty(), item->name);
setupExportedPropertiesScriptValue(itemScriptValue, item->properties, engine);
- QScriptValue childrenScriptValue = engine->newArray(static_cast<uint>(item->children.size()));
- itemScriptValue.setProperty(childItemsProperty(), childrenScriptValue);
+ JSValue childrenScriptValue = JS_NewArray(engine->context());
quint32 arrayIndex = 0;
for (const auto &childItem : item->children) {
- childrenScriptValue.setProperty(arrayIndex++,
- setupExportedItemScriptValue(childItem.get(), engine));
+ JS_SetPropertyUint32(engine->context(), childrenScriptValue, arrayIndex++,
+ setupExportedItemScriptValue(childItem.get(), engine));
}
+ setJsProperty(engine->context(), itemScriptValue, childItemsProperty(), childrenScriptValue);
return itemScriptValue;
}
-static QScriptValue setupExportsScriptValue(const ExportedModule &module, ScriptEngine *engine)
+static JSValue setupExportsScriptValue(const ResolvedProduct *product, ScriptEngine *engine)
{
- QScriptValue exportsScriptValue = engine->newObject();
- for (auto it = module.propertyValues.cbegin(); it != module.propertyValues.cend(); ++it)
- exportsScriptValue.setProperty(it.key(), engine->toScriptValue(it.value()));
+ const ExportedModule &module = product->exportedModule;
+ JSValue exportsScriptValue = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ attachPointerTo(exportsScriptValue, product);
+ for (auto it = module.propertyValues.cbegin(); it != module.propertyValues.cend(); ++it) {
+ setJsProperty(engine->context(), exportsScriptValue, it.key(),
+ engine->toScriptValue(it.value()));
+ }
setupExportedPropertiesScriptValue(exportsScriptValue, module.m_properties, engine);
- QScriptValue childrenScriptValue = engine->newArray(static_cast<uint>(module.children.size()));
- exportsScriptValue.setProperty(childItemsProperty(), childrenScriptValue);
+ JSValue childrenScriptValue = JS_NewArray(engine->context());
quint32 arrayIndex = 0;
for (const auto &exportedItem : module.children) {
- childrenScriptValue.setProperty(arrayIndex++,
- setupExportedItemScriptValue(exportedItem.get(), engine));
+ JS_SetPropertyUint32(engine->context(), childrenScriptValue, arrayIndex++,
+ setupExportedItemScriptValue(exportedItem.get(), engine));
}
- QScriptValue importsScriptValue = engine->newArray(module.importStatements.size());
- exportsScriptValue.setProperty(StringConstants::importsProperty(), importsScriptValue);
+ setJsProperty(engine->context(), exportsScriptValue, childItemsProperty(), childrenScriptValue);
+ JSValue importsScriptValue = JS_NewArray(engine->context());
arrayIndex = 0;
- for (const QString &importStatement : module.importStatements)
- importsScriptValue.setProperty(arrayIndex++, importStatement);
+ for (const QString &importStatement : module.importStatements) {
+ JS_SetPropertyUint32(engine->context(), importsScriptValue, arrayIndex++,
+ makeJsString(engine->context(), importStatement));
+ }
+ setJsProperty(engine->context(), exportsScriptValue, StringConstants::importsProperty(),
+ importsScriptValue);
for (auto it = module.modulePropertyValues.cbegin(); it != module.modulePropertyValues.cend();
++it) {
const QVariantMap entries = it.value().toMap();
if (entries.empty())
continue;
- QScriptValue moduleObject = engine->newObject();
- ModuleProperties::setModuleScriptValue(exportsScriptValue, moduleObject, it.key());
- for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt)
- moduleObject.setProperty(valIt.key(), engine->toScriptValue(valIt.value()));
+ JSValue moduleObject = engine->newObject();
+ ModuleProperties::setModuleScriptValue(engine, exportsScriptValue, moduleObject, it.key());
+ for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt) {
+ setJsProperty(engine->context(), moduleObject, valIt.key(),
+ engine->toScriptValue(valIt.value()));
+ }
}
return exportsScriptValue;
}
-static QScriptValue setupProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product)
+static void setupBaseProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product)
{
- QScriptValue &productScriptValue = engine->productScriptValuePrototype(product);
- if (productScriptValue.isValid())
- return productScriptValue;
- productScriptValue = engine->newObject();
- ModuleProperties::init(productScriptValue, product);
-
- QScriptValue artifactsFunc = engine->newFunction(&artifactsScriptValueForProduct, product);
- productScriptValue.setProperty(StringConstants::artifactsProperty(), artifactsFunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
-
- QScriptValue exportsScriptValue = setupExportsScriptValue(product->exportedModule, engine);
- DependenciesFunction(engine).init(productScriptValue, exportsScriptValue, product);
+ JSValue &productScriptValue = engine->baseProductScriptValue(product);
+ if (JS_IsObject(productScriptValue))
+ return;
+ const ScopedJsValue proto(engine->context(), JS_NewObject(engine->context()));
+ productScriptValue = JS_NewObjectProtoClass(engine->context(), proto,
+ engine->dataWithPtrClass());
+ attachPointerTo(productScriptValue, product);
+ ModuleProperties::init(engine, productScriptValue, product);
+
+ const QByteArray funcName = StringConstants::artifactsProperty().toUtf8();
+ const ScopedJsValue artifactsFunc(
+ engine->context(),
+ JS_NewCFunction(engine->context(), &artifactsScriptValueForProduct,
+ funcName.constData(), 0));
+ const ScopedJsAtom artifactsAtom(engine->context(), funcName);
+ JS_DefineProperty(engine->context(), productScriptValue, artifactsAtom,
+ JS_UNDEFINED, artifactsFunc, JS_UNDEFINED,
+ JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
+
+ // FIXME: Proper un-observe rather than ref count decrease here.
+ ScopedJsValue exportsScriptValue(engine->context(), setupExportsScriptValue(product, engine));
+ DependenciesFunction(engine).init(productScriptValue, exportsScriptValue);
+
+ // TODO: Why are these necessary? We catch accesses to product.exports in getProductProperty().
+ // But the exportsQbs() and exportsPkgConfig() tests fail without them.
engine->setObservedProperty(productScriptValue, exportsProperty(), exportsScriptValue);
- engine->observer()->addExportsObjectId(exportsScriptValue.objectId(), product);
- return productScriptValue;
+ engine->observer()->addExportsObjectId(jsObjectId(exportsScriptValue), product);
}
void setupScriptEngineForFile(ScriptEngine *engine, const FileContextBaseConstPtr &fileContext,
- QScriptValue targetObject, const ObserveMode &observeMode)
+ JSValue targetObject, const ObserveMode &observeMode)
{
engine->import(fileContext, targetObject, observeMode);
- JsExtensions::setupExtensions(fileContext->jsExtensions(), targetObject);
+ JsExtensions::setupExtensions(engine, fileContext->jsExtensions(), targetObject);
}
void setupScriptEngineForProduct(ScriptEngine *engine, ResolvedProduct *product,
- const ResolvedModule *module, QScriptValue targetObject,
+ const ResolvedModule *module, JSValue targetObject,
bool setBuildEnvironment)
{
- QScriptValue projectScriptValue = setupProjectScriptValue(engine, product->project.lock());
- targetObject.setProperty(StringConstants::projectVar(), projectScriptValue);
-
+ JSValue projectScriptValue = setupProjectScriptValue(engine, product->project.lock());
+ setJsProperty(engine->context(), targetObject, StringConstants::projectVar(),
+ projectScriptValue);
if (setBuildEnvironment) {
QVariant v;
v.setValue<void*>(&product->buildEnvironment);
engine->setProperty(StringConstants::qbsProcEnvVarInternal(), v);
}
- QScriptClass *scriptClass = engine->productPropertyScriptClass();
- if (!scriptClass) {
- scriptClass = new ProductPropertyScriptClass(engine);
- engine->setProductPropertyScriptClass(scriptClass);
+ JSClassID scriptClass = engine->productPropertyScriptClass();
+ if (scriptClass == 0) {
+ engine->setProductPropertyScriptClass(engine->registerClass("ProductProperties", nullptr,
+ nullptr, JS_UNDEFINED, &getProductPropertyNames, &getProductProperty));
+ scriptClass = engine->productPropertyScriptClass();
}
- QScriptValue productScriptValue = engine->newObject(scriptClass);
- productScriptValue.setPrototype(setupProductScriptValue(engine, product));
- targetObject.setProperty(StringConstants::productVar(), productScriptValue);
+ setupBaseProductScriptValue(engine, product);
+ JSValue productScriptValue = JS_NewObjectClass(engine->context(), scriptClass);
+ attachPointerTo(productScriptValue, product);
+ setJsProperty(engine->context(), targetObject, StringConstants::productVar(),
+ productScriptValue);
- QScriptValue data = getDataForProductScriptValue(engine, product);
+ JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
// If the Rule is in a Module, set up the 'moduleName' property
- if (!module->name.isEmpty())
- data.setProperty(ModuleNameKey, module->name);
- productScriptValue.setData(data);
+ if (!module->name.isEmpty()) {
+ JS_SetPropertyUint32(engine->context(), data, ModuleNameKey,
+ makeJsString(engine->context(), module->name));
+ }
+ defineJsProperty(engine->context(), productScriptValue,
+ StringConstants::dataPropertyInternal(), data);
}
bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode *> &path)
diff --git a/src/lib/corelib/buildgraph/buildgraph.h b/src/lib/corelib/buildgraph/buildgraph.h
index 2909b06bb..1eadb2e20 100644
--- a/src/lib/corelib/buildgraph/buildgraph.h
+++ b/src/lib/corelib/buildgraph/buildgraph.h
@@ -43,9 +43,10 @@
#include <language/forward_decls.h>
#include <tools/qbs_export.h>
-#include <QtCore/qstringlist.h>
+#include <quickjs.h>
-#include <QtScript/qscriptvalue.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/QVariantMap>
namespace qbs {
namespace Internal {
@@ -88,14 +89,18 @@ void removeGeneratedArtifactFromDisk(const QString &filePath, const Logger &logg
void disconnect(BuildGraphNode *u, BuildGraphNode *v);
void setupScriptEngineForFile(ScriptEngine *engine, const FileContextBaseConstPtr &fileContext,
- QScriptValue targetObject, const ObserveMode &observeMode);
+ JSValue targetObject, const ObserveMode &observeMode);
void setupScriptEngineForProduct(ScriptEngine *engine, ResolvedProduct *product,
- const ResolvedModule *module, QScriptValue targetObject,
+ const ResolvedModule *module, JSValue targetObject,
bool setBuildEnvironment);
QString relativeArtifactFileName(const Artifact *artifact); // Debugging helpers
void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger);
+void getPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ const QVariantMap &properties, const QStringList &extraPropertyNames,
+ JSValueConst extraObject);
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp
index ac6b1dc42..7b333d32f 100644
--- a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp
+++ b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp
@@ -46,37 +46,39 @@
namespace qbs {
namespace Internal {
-static QScriptValue toScriptValue(ScriptEngine *engine, const QString &productName,
- const QVariantMap &v, const QString &depName,
- const QualifiedId &moduleName)
+static JSValue toScriptValue(ScriptEngine *engine, const QString &productName,
+ const QVariantMap &v, const QString &depName,
+ const QualifiedId &moduleName)
{
- QScriptValue obj = engine->newObject();
+ JSValue obj = engine->newObject();
bool objIdAddedToObserver = false;
for (auto it = v.begin(); it != v.end(); ++it) {
if (it.value().userType() == QMetaType::QVariantMap) {
- obj.setProperty(it.key(), toScriptValue(engine, productName, it.value().toMap(),
- depName, QualifiedId(moduleName) << it.key()));
+ setJsProperty(engine->context(), obj, it.key(),
+ toScriptValue(engine, productName, it.value().toMap(),
+ depName, QualifiedId(moduleName) << it.key()));
} else {
if (!objIdAddedToObserver) {
objIdAddedToObserver = true;
- engine->observer()->addParameterObjectId(obj.objectId(), productName, depName,
+ engine->observer()->addParameterObjectId(jsObjectId(obj), productName, depName,
moduleName);
}
- engine->setObservedProperty(obj, it.key(), engine->toScriptValue(it.value()));
+ const ScopedJsValue val(engine->context(), engine->toScriptValue(it.value()));
+ engine->setObservedProperty(obj, it.key(), val);
}
}
return obj;
}
-static QScriptValue toScriptValue(ScriptEngine *scriptEngine, const QString &productName,
- const QVariantMap &v, const QString &depName)
+static JSValue toScriptValue(ScriptEngine *scriptEngine, const QString &productName,
+ const QVariantMap &v, const QString &depName)
{
return toScriptValue(scriptEngine, productName, v, depName, {});
}
-QScriptValue dependencyParametersValue(const QString &productName, const QString &dependencyName,
- const QVariantMap &parametersMap, ScriptEngine *engine)
+JSValue dependencyParametersValue(const QString &productName, const QString &dependencyName,
+ const QVariantMap &parametersMap, ScriptEngine *engine)
{
return toScriptValue(engine, productName, parametersMap, dependencyName);
}
diff --git a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h
index 7e4287be6..2fb4634e8 100644
--- a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h
+++ b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h
@@ -39,15 +39,16 @@
#ifndef QBS_DEPENDENCYPARAMETERSSCRIPTVALUE_H
#define QBS_DEPENDENCYPARAMETERSSCRIPTVALUE_H
+#include <quickjs.h>
+
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptvalue.h>
namespace qbs {
namespace Internal {
class ScriptEngine;
-QScriptValue dependencyParametersValue(const QString &productName, const QString &dependencyName,
- const QVariantMap &parametersMap, ScriptEngine *engine);
+JSValue dependencyParametersValue(const QString &productName, const QString &dependencyName,
+ const QVariantMap &parametersMap, ScriptEngine *engine);
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/depscanner.cpp b/src/lib/corelib/buildgraph/depscanner.cpp
index b24ec2d91..e4e10f477 100644
--- a/src/lib/corelib/buildgraph/depscanner.cpp
+++ b/src/lib/corelib/buildgraph/depscanner.cpp
@@ -56,7 +56,7 @@
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptcontext.h>
+#include <vector>
namespace qbs {
namespace Internal {
@@ -158,10 +158,9 @@ UserDependencyScanner::UserDependencyScanner(ResolvedScannerConstPtr scanner,
ScriptEngine *engine)
: m_scanner(std::move(scanner)),
m_engine(engine),
+ m_global(engine->context(), JS_NewObjectProto(engine->context(), m_engine->globalObject())),
m_product(nullptr)
{
- m_global = m_engine->newObject();
- m_global.setPrototype(m_engine->globalObject());
setupScriptEngineForFile(m_engine, m_scanner->scanScript.fileContext(), m_global,
ObserveMode::Disabled); // TODO: QBS-1092
}
@@ -218,7 +217,7 @@ public:
}
};
-QStringList UserDependencyScanner::evaluate(const Artifact *artifact,
+QStringList UserDependencyScanner::evaluate(Artifact *artifact,
const FileResourceBase *fileToScan, const PrivateScriptFunction &script)
{
ScriptEngineActiveFlagGuard guard(m_engine);
@@ -229,38 +228,42 @@ QStringList UserDependencyScanner::evaluate(const Artifact *artifact,
m_scanner->module.get(), m_global, true);
}
- QScriptValueList args;
+ JSValueList args;
args.reserve(fileToScan ? 4 : 3);
- args.push_back(m_global.property(StringConstants::projectVar()));
- args.push_back(m_global.property(StringConstants::productVar()));
+ args.push_back(getJsProperty(m_engine->context(), m_global, StringConstants::projectVar()));
+ args.push_back(getJsProperty(m_engine->context(), m_global, StringConstants::productVar()));
args.push_back(Transformer::translateFileConfig(m_engine, artifact, m_scanner->module->name));
if (fileToScan)
- args.push_back(fileToScan->filePath());
+ args.push_back(makeJsString(m_engine->context(), fileToScan->filePath()));
+ const ScopedJsValueList argsMgr(m_engine->context(), args);
- m_engine->setGlobalObject(m_global);
- QScriptValue &function = script.scriptFunction;
- if (!function.isValid() || function.engine() != m_engine) {
- function = m_engine->evaluate(script.sourceCode());
- if (Q_UNLIKELY(!function.isFunction()))
+ const TemporaryGlobalObjectSetter gos(m_engine, m_global);
+ JSValue &function = script.scriptFunction;
+ if (!JS_IsFunction(m_engine->context(), function)) {
+ function = m_engine->evaluate(JsValueOwner::ScriptEngine, script.sourceCode());
+ if (Q_UNLIKELY(!JS_IsFunction(m_engine->context(), function)))
throw ErrorInfo(Tr::tr("Invalid scan script."), script.location());
}
- QScriptValue result = function.call(QScriptValue(), args);
- m_engine->setGlobalObject(m_global.prototype());
+ const ScopedJsValue result(
+ m_engine->context(),
+ JS_Call(m_engine->context(), function, m_engine->globalObject(),
+ int(args.size()), args.data()));
m_engine->clearRequestedProperties();
- if (Q_UNLIKELY(m_engine->hasErrorOrException(result))) {
- QString msg = Tr::tr("evaluating scan script: ") + m_engine->lastErrorString(result);
- const CodeLocation loc = m_engine->lastErrorLocation(result, script.location());
- m_engine->clearExceptions();
- throw ErrorInfo(msg, loc);
+ if (JsException ex = m_engine->checkAndClearException(script.location())) {
+ ErrorInfo err = ex.toErrorInfo();
+ err.prepend(Tr::tr("Error evaluating scan script"));
+ throw err;
}
QStringList list;
- if (result.isArray()) {
- const int count = result.property(StringConstants::lengthProperty()).toInt32();
+ if (JS_IsArray(m_engine->context(), result)) {
+ const int count = getJsIntProperty(m_engine->context(), result,
+ StringConstants::lengthProperty());
list.reserve(count);
for (qint32 i = 0; i < count; ++i) {
- QScriptValue item = result.property(i);
- if (item.isValid() && !item.isUndefined())
- list.push_back(item.toString());
+ JSValue item = JS_GetPropertyUint32(m_engine->context(), result, i);
+ if (!JS_IsUninitialized(item) && !JS_IsUndefined(item))
+ list.push_back(getJsString(m_engine->context(), item));
+ JS_FreeValue(m_engine->context(), item);
}
}
return list;
diff --git a/src/lib/corelib/buildgraph/depscanner.h b/src/lib/corelib/buildgraph/depscanner.h
index 6b18004f9..51816dbd7 100644
--- a/src/lib/corelib/buildgraph/depscanner.h
+++ b/src/lib/corelib/buildgraph/depscanner.h
@@ -43,11 +43,10 @@
#include <language/forward_decls.h>
#include <language/filetags.h>
#include <language/preparescriptobserver.h>
+#include <tools/scripttools.h>
#include <QtCore/qstringlist.h>
-#include <QtScript/qscriptvalue.h>
-
class ScannerPlugin;
namespace qbs {
@@ -115,11 +114,11 @@ private:
const PropertyMapConstPtr &m2) const override;
bool cacheIsPerFile() const override { return true; }
- QStringList evaluate(const Artifact *artifact, const FileResourceBase *fileToScan, const PrivateScriptFunction &script);
+ QStringList evaluate(Artifact *artifact, const FileResourceBase *fileToScan, const PrivateScriptFunction &script);
ResolvedScannerConstPtr m_scanner;
ScriptEngine *m_engine;
- QScriptValue m_global;
+ ScopedJsValue m_global;
ResolvedProduct *m_product;
};
diff --git a/src/lib/corelib/buildgraph/environmentscriptrunner.cpp b/src/lib/corelib/buildgraph/environmentscriptrunner.cpp
index 841f39a8d..c4810f621 100644
--- a/src/lib/corelib/buildgraph/environmentscriptrunner.cpp
+++ b/src/lib/corelib/buildgraph/environmentscriptrunner.cpp
@@ -51,6 +51,8 @@
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
+#include <quickjs.h>
+
#include <QtCore/qhash.h>
#include <QtCore/qvariant.h>
@@ -165,38 +167,40 @@ void EnvironmentScriptRunner::setupEnvironment()
continue;
RulesEvaluationContext::Scope s(m_evalContext);
- QScriptValue envScriptContext = engine()->newObject();
- envScriptContext.setPrototype(engine()->globalObject());
+ JSContext * const ctx = engine()->context();
+ ScopedJsValue envScriptContext(ctx, JS_NewObjectProto(ctx, engine()->globalObject()));
setupScriptEngineForProduct(engine(), m_product, module, envScriptContext, false);
const QString &productKey = StringConstants::productVar();
const QString &projectKey = StringConstants::projectVar();
- m_evalContext->scope().setProperty(productKey, envScriptContext.property(productKey));
- m_evalContext->scope().setProperty(projectKey, envScriptContext.property(projectKey));
+ setJsProperty(ctx, m_evalContext->scope(), productKey,
+ getJsProperty(ctx, envScriptContext, productKey));
+ setJsProperty(ctx, m_evalContext->scope(), projectKey,
+ getJsProperty(ctx, envScriptContext, projectKey));
if (m_envType == RunEnv) {
- QScriptValue configArray = engine()->newArray(m_runEnvConfig.size());
+ JSValue configArray = JS_NewArray(ctx);
for (int i = 0; i < m_runEnvConfig.size(); ++i)
- configArray.setProperty(i, QScriptValue(m_runEnvConfig.at(i)));
- m_evalContext->scope().setProperty(QStringLiteral("config"), configArray);
+ JS_SetPropertyUint32(ctx, configArray, i, makeJsString(ctx, m_runEnvConfig.at(i)));
+ JS_SetPropertyStr(ctx, m_evalContext->scope(), "config", configArray);
}
setupScriptEngineForFile(engine(), setupScript.fileContext(), m_evalContext->scope(),
ObserveMode::Disabled);
// TODO: Cache evaluate result
- QScriptValue fun = engine()->evaluate(setupScript.sourceCode(),
- setupScript.location().filePath(),
- setupScript.location().line());
- QBS_CHECK(fun.isFunction());
- const QScriptValueList svArgs = ScriptEngine::argumentList(scriptFunctionArgs,
- m_evalContext->scope());
- const QScriptValue res = fun.call(QScriptValue(), svArgs);
- engine()->releaseResourcesOfScriptObjects();
- if (Q_UNLIKELY(engine()->hasErrorOrException(res))) {
+ ScopedJsValue fun(ctx, engine()->evaluate(JsValueOwner::Caller, setupScript.sourceCode(),
+ setupScript.location().filePath(),
+ setupScript.location().line()));
+ QBS_CHECK(JS_IsFunction(ctx, fun));
+ const ScopedJsValueList svArgs = engine()->argumentList(scriptFunctionArgs,
+ m_evalContext->scope());
+ JSValueList argsForFun = svArgs;
+ JS_Call(ctx, fun, engine()->globalObject(), int(argsForFun.size()), argsForFun.data());
+ if (const JsException ex = engine()->checkAndClearException(setupScript.location())) {
const QString scriptName = m_envType == BuildEnv
? StringConstants::setupBuildEnvironmentProperty()
: StringConstants::setupRunEnvironmentProperty();
- throw ErrorInfo(Tr::tr("Error running %1 script for product '%2': %3")
- .arg(scriptName, m_product->fullDisplayName(),
- engine()->lastErrorString(res)),
- engine()->lastErrorLocation(res, setupScript.location()));
+ ErrorInfo err = ex.toErrorInfo();
+ err.prepend(Tr::tr("Error running %1 script for product '%2'")
+ .arg(scriptName, m_product->fullDisplayName()));
+ throw err;
}
}
diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.cpp b/src/lib/corelib/buildgraph/jscommandexecutor.cpp
index abebc82b8..4d92bf7c3 100644
--- a/src/lib/corelib/buildgraph/jscommandexecutor.cpp
+++ b/src/lib/corelib/buildgraph/jscommandexecutor.cpp
@@ -40,7 +40,6 @@
#include "jscommandexecutor.h"
-#include "artifact.h"
#include "buildgraph.h"
#include "rulecommands.h"
#include "transformer.h"
@@ -55,7 +54,10 @@
#include <tools/qbsassert.h>
#include <tools/qttools.h>
+#include <quickjs.h>
+
#include <QtCore/qeventloop.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qpointer.h>
#include <QtCore/qthread.h>
#include <QtCore/qtimer.h>
@@ -87,10 +89,11 @@ public:
void cancel(const qbs::ErrorInfo &reason)
{
+ QMutexLocker locker(&m_resultMutex);
m_result.success = !reason.hasError();
m_result.errorMessage = reason.toString();
if (m_scriptEngine)
- m_scriptEngine->abortEvaluation();
+ m_scriptEngine->cancel();
m_cancelled = true;
}
@@ -122,36 +125,30 @@ private:
m_result.success = true;
m_result.errorMessage.clear();
ScriptEngine * const scriptEngine = provideScriptEngine();
- QScriptValue scope = scriptEngine->newObject();
- scope.setPrototype(scriptEngine->globalObject());
- m_scriptEngine->clearRequestedProperties();
+ JSContext * const ctx = scriptEngine->context();
+ const ScopedJsValue scope(ctx, JS_NewObject(scriptEngine->context()));
setupScriptEngineForFile(scriptEngine,
transformer->rule->prepareScript.fileContext(), scope,
ObserveMode::Enabled);
-
- QScriptValue importScopeForSourceCode;
+ ScopedJsValue importScopeForSourceCode(ctx, JS_UNDEFINED);
if (!cmd->scopeName().isEmpty())
- importScopeForSourceCode = scope.property(cmd->scopeName());
+ importScopeForSourceCode.setValue(getJsProperty(ctx, scope, cmd->scopeName()));
setupScriptEngineForProduct(scriptEngine, transformer->product().get(),
transformer->rule->module.get(), scope, true);
- transformer->setupInputs(scope);
- transformer->setupOutputs(scope);
- transformer->setupExplicitlyDependsOn(scope);
-
- for (QVariantMap::const_iterator it = cmd->properties().constBegin();
- it != cmd->properties().constEnd(); ++it) {
- scope.setProperty(it.key(), scriptEngine->toScriptValue(it.value()));
- }
-
- scriptEngine->setGlobalObject(scope);
- if (importScopeForSourceCode.isObject())
- scriptEngine->currentContext()->pushScope(importScopeForSourceCode);
- scriptEngine->evaluate(cmd->sourceCode());
- scriptEngine->releaseResourcesOfScriptObjects();
- if (importScopeForSourceCode.isObject())
- scriptEngine->currentContext()->popScope();
- scriptEngine->setGlobalObject(scope.prototype());
+ transformer->setupInputs(scriptEngine, scope);
+ transformer->setupOutputs(scriptEngine, scope);
+ transformer->setupExplicitlyDependsOn(scriptEngine, scope);
+
+ for (auto it = cmd->properties().constBegin(); it != cmd->properties().constEnd(); ++it)
+ setJsProperty(ctx, scope, it.key(), scriptEngine->toScriptValue(it.value()));
+
+ const TemporaryGlobalObjectSetter gos(scriptEngine, scope);
+ JSValueList scopeChain;
+ if (JS_IsObject(importScopeForSourceCode))
+ scopeChain << importScopeForSourceCode;
+ const ScopedJsValue res(ctx, scriptEngine->evaluate(JsValueOwner::Caller, cmd->sourceCode(),
+ {}, 1, scopeChain));
transformer->propertiesRequestedInCommands
+= scriptEngine->propertiesRequestedInScript();
unite(transformer->propertiesRequestedFromArtifactInCommands,
@@ -168,14 +165,17 @@ private:
std::make_pair(p->uniqueName(), p->exportedModule));
}
scriptEngine->clearRequestedProperties();
- if (scriptEngine->hasUncaughtException()) {
+ if (const JsException exception = scriptEngine->checkAndClearException(cmd->codeLocation())) {
// ### We don't know the line number of the command's sourceCode property assignment.
- setError(scriptEngine->uncaughtException().toString(), cmd->codeLocation());
+ setError(exception.message(), cmd->codeLocation());
}
}
void setError(const QString &errorMessage, const CodeLocation &codeLocation)
{
+ QMutexLocker locker(&m_resultMutex);
+ if (m_cancelled)
+ return;
m_result.success = false;
m_result.errorMessage = errorMessage;
m_result.errorLocation = codeLocation;
@@ -185,12 +185,15 @@ private:
{
if (!m_scriptEngine)
m_scriptEngine = ScriptEngine::create(m_logger, EvalContext::JsCommand);
+ else
+ m_scriptEngine->reset();
return m_scriptEngine.get();
}
Logger m_logger;
std::unique_ptr<ScriptEngine> m_scriptEngine;
JavaScriptCommandResult m_result;
+ QMutex m_resultMutex;
bool m_running = false;
bool m_cancelled = false;
};
@@ -258,11 +261,8 @@ bool JsCommandExecutor::doStart()
void JsCommandExecutor::cancel(const qbs::ErrorInfo &reason)
{
- if (m_running && (!dryRun() || command()->ignoreDryRun()))
- QTimer::singleShot(0, m_objectInThread, [objectInThread = QPointer<JsCommandExecutorThreadObject>{m_objectInThread}, reason] {
- if (objectInThread)
- objectInThread->cancel(reason);
- });
+ if (m_running && (!dryRun() || command()->ignoreDryRun()) && m_objectInThread)
+ m_objectInThread->cancel(reason);
}
void JsCommandExecutor::onJavaScriptCommandFinished()
diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
index e687342e1..0058b940e 100644
--- a/src/lib/corelib/buildgraph/processcommandexecutor.cpp
+++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
@@ -61,12 +61,12 @@
#include <tools/shellutils.h>
#include <tools/stringconstants.h>
+#include <quickjs.h>
+
#include <QtCore/qdir.h>
#include <QtCore/qtemporaryfile.h>
#include <QtCore/qtimer.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
@@ -215,34 +215,39 @@ QString ProcessCommandExecutor::filterProcessOutput(const QByteArray &_output,
if (filterFunctionSource.isEmpty())
return output;
- QScriptValue scope = scriptEngine()->newObject();
- scope.setPrototype(scriptEngine()->globalObject());
- for (QVariantMap::const_iterator it = command()->properties().constBegin();
- it != command()->properties().constEnd(); ++it) {
- scope.setProperty(it.key(), scriptEngine()->toScriptValue(it.value()));
+ JSContext * const ctx = scriptEngine()->context();
+ const ScopedJsValue scope(ctx, JS_NewObjectProto(ctx, scriptEngine()->globalObject()));
+ for (auto it = command()->properties().constBegin();
+ it != command()->properties().constEnd(); ++it) {
+ setJsProperty(ctx, scope, it.key(), scriptEngine()->toScriptValue(it.value()));
}
- TemporaryGlobalObjectSetter tgos(scope);
- QScriptValue filterFunction = scriptEngine()->evaluate(QLatin1String("var f = ")
- + filterFunctionSource
- + QLatin1String("; f"));
- if (!filterFunction.isFunction()) {
+ TemporaryGlobalObjectSetter tgos(scriptEngine(), scope);
+ const ScopedJsValue filterFunction(
+ ctx,
+ scriptEngine()->evaluate(JsValueOwner::Caller,
+ QLatin1String("var f = ")
+ + filterFunctionSource
+ + QLatin1String("; f")));
+ if (!JS_IsFunction(scriptEngine()->context(), filterFunction)) {
logger().printWarning(ErrorInfo(Tr::tr("Error in filter function: %1.\n%2")
- .arg(filterFunctionSource, filterFunction.toString())));
+ .arg(filterFunctionSource,
+ getJsString(scriptEngine()->context(), filterFunction))));
return output;
}
- QScriptValue outputArg = scriptEngine()->newArray(1);
- outputArg.setProperty(0, scriptEngine()->toScriptValue(output));
- QScriptValue filteredOutput = filterFunction.call(scriptEngine()->undefinedValue(), outputArg);
- if (scriptEngine()->hasErrorOrException(filteredOutput)) {
- logger().printWarning(ErrorInfo(Tr::tr("Error when calling output filter function: %1")
- .arg(scriptEngine()->lastErrorString(filteredOutput)),
- scriptEngine()->lastErrorLocation(filteredOutput)));
+ const ScopedJsValue outputArg(ctx, scriptEngine()->toScriptValue(output));
+ JSValue outputArgForCall = outputArg;
+ const ScopedJsValue filteredOutput(
+ ctx, JS_Call(ctx, filterFunction, JS_UNDEFINED, 1, &outputArgForCall));
+ if (const JsException ex = scriptEngine()->checkAndClearException({})) {
+ ErrorInfo err = ex.toErrorInfo();
+ err.prepend(Tr::tr("Error when calling output filter function"));
+ logger().printWarning(err);
return output;
}
- return filteredOutput.toString();
+ return getJsString(ctx, filteredOutput);
}
static QProcess::ProcessError saveToFile(const QString &filePath, const QByteArray &content)
diff --git a/src/lib/corelib/buildgraph/productbuilddata.cpp b/src/lib/corelib/buildgraph/productbuilddata.cpp
index db51b2b9f..6ec01a1ce 100644
--- a/src/lib/corelib/buildgraph/productbuilddata.cpp
+++ b/src/lib/corelib/buildgraph/productbuilddata.cpp
@@ -102,7 +102,6 @@ void ProductBuildData::addFileTagToArtifact(Artifact *artifact, const FileTag &t
ArtifactSetByFileTag ProductBuildData::artifactsByFileTag() const
{
- std::lock_guard<std::mutex> l(m_artifactsMapMutex);
return m_artifactsByFileTag;
}
@@ -124,7 +123,6 @@ void ProductBuildData::addRescuableArtifactData(const QString &filePath,
bool ProductBuildData::checkAndSetJsArtifactsMapUpToDateFlag()
{
- std::lock_guard<std::mutex> l(m_artifactsMapMutex);
if (!m_jsArtifactsMapUpToDate) {
m_jsArtifactsMapUpToDate = true;
return false;
diff --git a/src/lib/corelib/buildgraph/productbuilddata.h b/src/lib/corelib/buildgraph/productbuilddata.h
index a7660af27..f6c531713 100644
--- a/src/lib/corelib/buildgraph/productbuilddata.h
+++ b/src/lib/corelib/buildgraph/productbuilddata.h
@@ -86,6 +86,9 @@ public:
void setBuildPriority(unsigned int prio) { m_buildPriority = prio; }
bool checkAndSetJsArtifactsMapUpToDateFlag();
+ std::unique_lock<std::mutex> getArtifactsMapLock() {
+ return std::unique_lock(m_artifactsMapMutex);
+ }
template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
{
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.cpp b/src/lib/corelib/buildgraph/projectbuilddata.cpp
index e0e70e650..c229a6171 100644
--- a/src/lib/corelib/buildgraph/projectbuilddata.cpp
+++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp
@@ -473,7 +473,7 @@ ScriptEngine *BuildDataResolver::engine() const
return evalContext()->engine();
}
-QScriptValue BuildDataResolver::scope() const
+JSValue BuildDataResolver::scope() const
{
return evalContext()->scope();
}
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.h b/src/lib/corelib/buildgraph/projectbuilddata.h
index 930344435..416651912 100644
--- a/src/lib/corelib/buildgraph/projectbuilddata.h
+++ b/src/lib/corelib/buildgraph/projectbuilddata.h
@@ -47,11 +47,11 @@
#include <tools/set.h>
#include <tools/qttools.h>
+#include <quickjs.h>
+
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptvalue.h>
-
#include <unordered_map>
namespace qbs {
@@ -126,7 +126,7 @@ private:
RulesEvaluationContextPtr evalContext() const;
ScriptEngine *engine() const;
- QScriptValue scope() const;
+ JSValue scope() const;
TopLevelProjectPtr m_project;
Logger m_logger;
diff --git a/src/lib/corelib/buildgraph/qtmocscanner.cpp b/src/lib/corelib/buildgraph/qtmocscanner.cpp
index a911f4a50..7df84e52c 100644
--- a/src/lib/corelib/buildgraph/qtmocscanner.cpp
+++ b/src/lib/corelib/buildgraph/qtmocscanner.cpp
@@ -54,9 +54,6 @@
#include <QtCore/qdebug.h>
-#include <QtScript/qscriptcontext.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
@@ -102,21 +99,23 @@ private:
static QString qtMocScannerJsName() { return QStringLiteral("QtMocScanner"); }
-QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue)
- : m_tags(*commonFileTags())
+QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, ScriptEngine *engine, JSValue targetScriptValue)
+ : m_engine(engine)
+ , m_tags(*commonFileTags())
, m_product(product)
- , m_targetScriptValue(targetScriptValue)
+ , m_targetScriptValue(JS_DupValue(engine->context(), targetScriptValue))
{
- const auto engine = static_cast<ScriptEngine *>(targetScriptValue.engine());
- QScriptValue scannerObj = engine->newObject();
- targetScriptValue.setProperty(qtMocScannerJsName(), scannerObj);
- QScriptValue applyFunction = engine->newFunction(&js_apply, this);
- scannerObj.setProperty(QStringLiteral("apply"), applyFunction);
+ JSValue scannerObj = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ attachPointerTo(scannerObj, this);
+ setJsProperty(engine->context(), targetScriptValue, qtMocScannerJsName(), scannerObj);
+ JSValue applyFunction = JS_NewCFunction(engine->context(), &js_apply, "QtMocScanner", 1);
+ setJsProperty(engine->context(), scannerObj, QStringLiteral("apply"), applyFunction);
}
QtMocScanner::~QtMocScanner()
{
- m_targetScriptValue.setProperty(qtMocScannerJsName(), QScriptValue());
+ setJsProperty(m_engine->context(), m_targetScriptValue, qtMocScannerJsName(), JS_UNDEFINED);
+ JS_FreeValue(m_engine->context(), m_targetScriptValue);
}
static RawScanResult runScanner(ScannerPlugin *scanner, const Artifact *artifact)
@@ -199,22 +198,24 @@ void QtMocScanner::findIncludedMocCppFiles()
}
}
-QScriptValue QtMocScanner::js_apply(QScriptContext *ctx, QScriptEngine *engine,
- QtMocScanner *that)
+JSValue QtMocScanner::js_apply(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
{
- QScriptValue input = ctx->argument(0);
- return that->apply(engine, attachedPointer<Artifact>(input));
+ if (argc < 1)
+ return throwError(ctx, Tr::tr("QtMocScanner.apply() requires an argument."));
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto scanner = attachedPointer<QtMocScanner>(this_val, engine->dataWithPtrClass());
+ return scanner->apply(engine, attachedPointer<Artifact>(argv[0], engine->dataWithPtrClass()));
}
-static QScriptValue scannerCountError(QScriptEngine *engine, size_t scannerCount,
- const QString &fileTag)
+static JSValue scannerCountError(ScriptEngine *engine, size_t scannerCount,
+ const QString &fileTag)
{
- return engine->currentContext()->throwError(
- Tr::tr("There are %1 scanners for the file tag %2. "
- "Expected is exactly one.").arg(scannerCount).arg(fileTag));
+ return throwError(engine->context(),
+ Tr::tr("There are %1 scanners for the file tag %2. "
+ "Expected is exactly one.").arg(scannerCount).arg(fileTag));
}
-QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact)
+JSValue QtMocScanner::apply(ScriptEngine *engine, const Artifact *artifact)
{
if (!m_cppScanner) {
auto scanners = ScannerPluginManager::scannersForFileTag(m_tags.cpp);
@@ -266,11 +267,13 @@ QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact
<< "mustCompile:" << mustCompile
<< "hasPluginMetaDataMacro:" << hasPluginMetaDataMacro;
- QScriptValue obj = engine->newObject();
- obj.setProperty(QStringLiteral("hasQObjectMacro"), hasQObjectMacro);
- obj.setProperty(QStringLiteral("mustCompile"), mustCompile);
- obj.setProperty(QStringLiteral("hasPluginMetaDataMacro"), hasPluginMetaDataMacro);
- static_cast<ScriptEngine *>(engine)->setUsesIo();
+ JSValue obj = engine->newObject();
+ JSContext * const ctx = m_engine->context();
+ setJsProperty(ctx, obj, QStringLiteral("hasQObjectMacro"), JS_NewBool(ctx, hasQObjectMacro));
+ setJsProperty(ctx, obj, QStringLiteral("mustCompile"), JS_NewBool(ctx, mustCompile));
+ setJsProperty(ctx, obj, QStringLiteral("hasPluginMetaDataMacro"),
+ JS_NewBool(ctx, hasPluginMetaDataMacro));
+ engine->setUsesIo();
return obj;
}
diff --git a/src/lib/corelib/buildgraph/qtmocscanner.h b/src/lib/corelib/buildgraph/qtmocscanner.h
index 6455383f3..2e2a00007 100644
--- a/src/lib/corelib/buildgraph/qtmocscanner.h
+++ b/src/lib/corelib/buildgraph/qtmocscanner.h
@@ -41,12 +41,11 @@
#define QBS_QTMOCSCANNER_H
#include <language/language.h>
+#include <quickjs.h>
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptvalue.h>
-
QT_BEGIN_NAMESPACE
class QScriptContext;
QT_END_NAMESPACE
@@ -55,6 +54,7 @@ class ScannerPlugin;
namespace qbs {
namespace Internal {
+class ScriptEngine;
class Artifact;
struct CommonFileTags;
@@ -62,17 +62,19 @@ struct CommonFileTags;
class QtMocScanner
{
public:
- explicit QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue);
+ explicit QtMocScanner(const ResolvedProductPtr &product, ScriptEngine *engine,
+ JSValue targetScriptValue);
~QtMocScanner();
private:
void findIncludedMocCppFiles();
- static QScriptValue js_apply(QScriptContext *ctx, QScriptEngine *engine, QtMocScanner *that);
- QScriptValue apply(QScriptEngine *engine, const Artifact *artifact);
+ static JSValue js_apply(JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
+ JSValue apply(ScriptEngine *engine, const Artifact *artifact);
+ ScriptEngine * const m_engine;
const CommonFileTags &m_tags;
const ResolvedProductPtr &m_product;
- QScriptValue m_targetScriptValue;
+ JSValue m_targetScriptValue;
QHash<QString, QString> m_includedMocCppFiles;
ScannerPlugin *m_cppScanner = nullptr;
};
diff --git a/src/lib/corelib/buildgraph/rulecommands.cpp b/src/lib/corelib/buildgraph/rulecommands.cpp
index 9eed7c715..6acd1d68c 100644
--- a/src/lib/corelib/buildgraph/rulecommands.cpp
+++ b/src/lib/corelib/buildgraph/rulecommands.cpp
@@ -39,6 +39,8 @@
****************************************************************************/
#include "rulecommands.h"
+
+#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
@@ -48,9 +50,6 @@
#include <QtCore/qfile.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalueiterator.h>
-
namespace qbs {
namespace Internal {
@@ -79,10 +78,11 @@ static QString stdoutFilterFunctionProperty() { return QStringLiteral("stdoutFil
static QString timeoutProperty() { return QStringLiteral("timeout"); }
static QString workingDirProperty() { return QStringLiteral("workingDirectory"); }
-static QString invokedSourceCode(const QScriptValue &codeOrFunction)
+static QString invokedSourceCode(JSContext *ctx, const JSValue &codeOrFunction)
{
- const QString &code = codeOrFunction.toString();
- return codeOrFunction.isFunction() ? QStringLiteral("(") + code + QStringLiteral(")()") : code;
+ const QString &code = getJsString(ctx, codeOrFunction);
+ return JS_IsFunction(ctx, codeOrFunction)
+ ? QStringLiteral("(") + code + QStringLiteral(")()") : code;
}
AbstractCommand::AbstractCommand()
@@ -110,17 +110,18 @@ bool AbstractCommand::equals(const AbstractCommand *other) const
&& m_properties == other->m_properties;
}
-void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
+void AbstractCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
+ const CodeLocation &codeLocation)
{
- m_description = scriptValue->property(StringConstants::descriptionProperty()).toString();
- m_extendedDescription = scriptValue->property(extendedDescriptionProperty()).toString();
- m_highlight = scriptValue->property(highlightProperty()).toString();
- m_ignoreDryRun = scriptValue->property(ignoreDryRunProperty()).toBool();
- m_silent = scriptValue->property(silentProperty()).toBool();
- m_jobPool = scriptValue->property(StringConstants::jobPoolProperty()).toString();
- const auto timeoutScriptValue = scriptValue->property(timeoutProperty());
- if (!timeoutScriptValue.isUndefined() && !timeoutScriptValue.isNull())
- m_timeout = timeoutScriptValue.toInt32();
+ m_description = getJsStringProperty(ctx, *scriptValue, StringConstants::descriptionProperty());
+ m_extendedDescription = getJsStringProperty(ctx, *scriptValue, extendedDescriptionProperty());
+ m_highlight = getJsStringProperty(ctx, *scriptValue, highlightProperty());
+ m_ignoreDryRun = getJsBoolProperty(ctx, *scriptValue, ignoreDryRunProperty());
+ m_silent = getJsBoolProperty(ctx, *scriptValue, silentProperty());
+ m_jobPool = getJsStringProperty(ctx, *scriptValue, StringConstants::jobPoolProperty());
+ const auto timeoutScriptValue = getJsProperty(ctx, *scriptValue, timeoutProperty());
+ if (!JS_IsUndefined(timeoutScriptValue) && !JS_IsNull(timeoutScriptValue))
+ m_timeout = JS_VALUE_GET_INT(timeoutScriptValue);
m_codeLocation = codeLocation;
m_predefinedProperties
@@ -148,94 +149,95 @@ void AbstractCommand::store(PersistentPool &pool)
serializationOp<PersistentPool::Store>(pool);
}
-void AbstractCommand::applyCommandProperties(const QScriptValue *scriptValue)
+void AbstractCommand::applyCommandProperties(JSContext *ctx, const JSValue *scriptValue)
{
- QScriptValueIterator it(*scriptValue);
- while (it.hasNext()) {
- it.next();
- if (m_predefinedProperties.contains(it.name()))
- continue;
- const QVariant value = it.value().toVariant();
- if (QMetaType::Type(value.userType()) == QMetaType::QObjectStar
- || it.value().scriptClass()
- || it.value().data().isValid()) {
+ handleJsProperties(ctx, *scriptValue, [this, ctx](const JSAtom &prop, const JSPropertyDescriptor &desc) {
+ const QString name = getJsString(ctx, prop);
+ if (m_predefinedProperties.contains(name))
+ return;
+ // TODO: Use script class for command objects, don't allow setting random properties
+ if (!isSimpleValue(desc.value)) {
throw ErrorInfo(Tr::tr("Property '%1' has a type unsuitable for storing in a command "
- "object.").arg(it.name()), m_codeLocation);
+ "object.").arg(name), m_codeLocation);
}
- m_properties.insert(it.name(), value);
- }
-}
-
-static QScriptValue js_CommandBase(QScriptContext *context, QScriptEngine *engine)
-{
- QScriptValue cmd = context->thisObject();
- QBS_ASSERT(context->isCalledAsConstructor(), cmd = engine->newObject());
- cmd.setProperty(StringConstants::descriptionProperty(),
- engine->toScriptValue(AbstractCommand::defaultDescription()));
- cmd.setProperty(extendedDescriptionProperty(),
- engine->toScriptValue(AbstractCommand::defaultExtendedDescription()));
- cmd.setProperty(highlightProperty(),
- engine->toScriptValue(AbstractCommand::defaultHighLight()));
- cmd.setProperty(ignoreDryRunProperty(),
- engine->toScriptValue(AbstractCommand::defaultIgnoreDryRun()));
- cmd.setProperty(silentProperty(),
- engine->toScriptValue(AbstractCommand::defaultIsSilent()));
- cmd.setProperty(timeoutProperty(),
- engine->toScriptValue(AbstractCommand::defaultTimeout()));
+ m_properties.insert(name, getJsVariant(ctx, desc.value));
+ });
+}
+
+static JSValue js_CommandBase(JSContext *ctx)
+{
+ const JSValue cmd = JS_NewObject(ctx);
+ setJsProperty(ctx, cmd, StringConstants::descriptionProperty(),
+ makeJsString(ctx, AbstractCommand::defaultDescription()));
+ setJsProperty(ctx, cmd, extendedDescriptionProperty(),
+ makeJsString(ctx, AbstractCommand::defaultExtendedDescription()));
+ setJsProperty(ctx, cmd, highlightProperty(),
+ makeJsString(ctx, AbstractCommand::defaultHighLight()));
+ setJsProperty(ctx, cmd, ignoreDryRunProperty(),
+ JS_NewBool(ctx, AbstractCommand::defaultIgnoreDryRun()));
+ setJsProperty(ctx, cmd, silentProperty(),
+ JS_NewBool(ctx, AbstractCommand::defaultIsSilent()));
+ setJsProperty(ctx, cmd, timeoutProperty(),
+ JS_NewInt32(ctx, AbstractCommand::defaultTimeout()));
return cmd;
}
-static QScriptValue js_Command(QScriptContext *context, QScriptEngine *engine)
+static JSValue js_Command(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int)
{
- if (Q_UNLIKELY(!context->isCalledAsConstructor()))
- return context->throwError(Tr::tr("Command constructor called without new."));
-
static ProcessCommandPtr commandPrototype = ProcessCommand::create();
-
- QScriptValue program = context->argument(0);
- if (program.isUndefined())
- program = engine->toScriptValue(commandPrototype->program());
- QScriptValue arguments = context->argument(1);
- if (arguments.isUndefined())
- arguments = engine->toScriptValue(commandPrototype->arguments());
- QScriptValue cmd = js_CommandBase(context, engine);
- cmd.setProperty(StringConstants::classNameProperty(),
- engine->toScriptValue(StringConstants::commandType()));
- cmd.setProperty(programProperty(), program);
- cmd.setProperty(argumentsProperty(), arguments);
- cmd.setProperty(workingDirProperty(),
- engine->toScriptValue(commandPrototype->workingDir()));
- cmd.setProperty(maxExitCodeProperty(),
- engine->toScriptValue(commandPrototype->maxExitCode()));
- cmd.setProperty(stdoutFilterFunctionProperty(),
- engine->toScriptValue(commandPrototype->stdoutFilterFunction()));
- cmd.setProperty(stderrFilterFunctionProperty(),
- engine->toScriptValue(commandPrototype->stderrFilterFunction()));
- cmd.setProperty(responseFileThresholdProperty(),
- engine->toScriptValue(commandPrototype->responseFileThreshold()));
- cmd.setProperty(responseFileArgumentIndexProperty(),
- engine->toScriptValue(commandPrototype->responseFileArgumentIndex()));
- cmd.setProperty(responseFileUsagePrefixProperty(),
- engine->toScriptValue(commandPrototype->responseFileUsagePrefix()));
- cmd.setProperty(responseFileSeparatorProperty(),
- engine->toScriptValue(commandPrototype->responseFileSeparator()));
- cmd.setProperty(stdoutFilePathProperty(),
- engine->toScriptValue(commandPrototype->stdoutFilePath()));
- cmd.setProperty(stderrFilePathProperty(),
- engine->toScriptValue(commandPrototype->stderrFilePath()));
- cmd.setProperty(environmentProperty(),
- engine->toScriptValue(commandPrototype->environment().toStringList()));
- cmd.setProperty(ignoreDryRunProperty(),
- engine->toScriptValue(commandPrototype->ignoreDryRun()));
+ JSValue program = JS_UNDEFINED;
+ if (argc > 0)
+ program = JS_DupValue(ctx, argv[0]);
+ if (JS_IsUndefined(program))
+ program = makeJsString(ctx, commandPrototype->program());
+ JSValue arguments = JS_UNDEFINED;
+ if (argc > 1)
+ arguments = JS_DupValue(ctx, argv[1]);
+ if (JS_IsUndefined(arguments))
+ arguments = makeJsStringList(ctx, commandPrototype->arguments());
+ if (JS_IsString(arguments)) {
+ JSValue args = JS_NewArray(ctx);
+ JS_SetPropertyUint32(ctx, args, 0, arguments);
+ arguments = args;
+ }
+ JSValue cmd = js_CommandBase(ctx);
+ setJsProperty(ctx, cmd, StringConstants::classNameProperty(),
+ makeJsString(ctx, StringConstants::commandType()));
+ setJsProperty(ctx, cmd, programProperty(), program);
+ setJsProperty(ctx, cmd, argumentsProperty(), arguments);
+ setJsProperty(ctx, cmd, workingDirProperty(),
+ makeJsString(ctx, commandPrototype->workingDir()));
+ setJsProperty(ctx, cmd, maxExitCodeProperty(),
+ JS_NewInt32(ctx, commandPrototype->maxExitCode()));
+ setJsProperty(ctx, cmd, stdoutFilterFunctionProperty(),
+ makeJsString(ctx, commandPrototype->stdoutFilterFunction()));
+ setJsProperty(ctx, cmd, stderrFilterFunctionProperty(),
+ makeJsString(ctx, commandPrototype->stderrFilterFunction()));
+ setJsProperty(ctx, cmd, responseFileThresholdProperty(),
+ JS_NewInt32(ctx, commandPrototype->responseFileThreshold()));
+ setJsProperty(ctx, cmd, responseFileArgumentIndexProperty(),
+ JS_NewInt32(ctx, commandPrototype->responseFileArgumentIndex()));
+ setJsProperty(ctx, cmd, responseFileUsagePrefixProperty(),
+ makeJsString(ctx, commandPrototype->responseFileUsagePrefix()));
+ setJsProperty(ctx, cmd, responseFileSeparatorProperty(),
+ makeJsString(ctx, commandPrototype->responseFileSeparator()));
+ setJsProperty(ctx, cmd, stdoutFilePathProperty(),
+ makeJsString(ctx, commandPrototype->stdoutFilePath()));
+ setJsProperty(ctx, cmd, stderrFilePathProperty(),
+ makeJsString(ctx, commandPrototype->stderrFilePath()));
+ setJsProperty(ctx, cmd, environmentProperty(),
+ makeJsStringList(ctx, commandPrototype->environment().toStringList()));
+ setJsProperty(ctx, cmd, ignoreDryRunProperty(),
+ JS_NewBool(ctx, commandPrototype->ignoreDryRun()));
return cmd;
}
-void ProcessCommand::setupForJavaScript(QScriptValue targetObject)
+void ProcessCommand::setupForJavaScript(ScriptEngine *engine, JSValue targetObject)
{
- QBS_CHECK(targetObject.isObject());
- QScriptValue ctor = targetObject.engine()->newFunction(js_Command, 2);
- targetObject.setProperty(StringConstants::commandType(), ctor);
+ engine->registerClass(StringConstants::commandType().toUtf8().constData(), js_Command, nullptr,
+ targetObject);
}
ProcessCommand::ProcessCommand()
@@ -289,40 +291,37 @@ bool ProcessCommand::equals(const AbstractCommand *otherAbstractCommand) const
&& m_environment == other->m_environment;
}
-void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
-{
- AbstractCommand::fillFromScriptValue(scriptValue, codeLocation);
- m_program = scriptValue->property(programProperty()).toString();
- m_arguments = scriptValue->property(argumentsProperty()).toVariant().toStringList();
- m_workingDir = scriptValue->property(workingDirProperty()).toString();
- m_maxExitCode = scriptValue->property(maxExitCodeProperty()).toInt32();
-
- // toString() is required, presumably due to QtScript bug that manifests itself on Windows
- const QScriptValue stdoutFilterFunction
- = scriptValue->property(stdoutFilterFunctionProperty()).toString();
-
- m_stdoutFilterFunction = invokedSourceCode(stdoutFilterFunction);
-
- // toString() is required, presumably due to QtScript bug that manifests itself on Windows
- const QScriptValue stderrFilterFunction
- = scriptValue->property(stderrFilterFunctionProperty()).toString();
-
- m_stderrFilterFunction = invokedSourceCode(stderrFilterFunction);
- m_relevantEnvVars = scriptValue->property(QStringLiteral("relevantEnvironmentVariables"))
- .toVariant().toStringList();
- m_responseFileThreshold = scriptValue->property(responseFileThresholdProperty())
- .toInt32();
- m_responseFileArgumentIndex = scriptValue->property(responseFileArgumentIndexProperty())
- .toInt32();
- m_responseFileUsagePrefix = scriptValue->property(responseFileUsagePrefixProperty())
- .toString();
- m_responseFileSeparator = scriptValue->property(responseFileSeparatorProperty())
- .toString();
- QStringList envList = scriptValue->property(environmentProperty()).toVariant()
- .toStringList();
+void ProcessCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, const CodeLocation &codeLocation)
+{
+ AbstractCommand::fillFromScriptValue(ctx, scriptValue, codeLocation);
+ m_program = getJsStringProperty(ctx, *scriptValue, programProperty());
+ m_arguments = getJsStringListProperty(ctx, *scriptValue, argumentsProperty());
+ m_workingDir = getJsStringProperty(ctx, *scriptValue, workingDirProperty());
+ m_maxExitCode = getJsIntProperty(ctx, *scriptValue, maxExitCodeProperty());
+
+ const ScopedJsValue stdoutFilterFunction(ctx, getJsProperty(ctx, *scriptValue,
+ stdoutFilterFunctionProperty()));
+ if (JS_IsFunction(ctx, stdoutFilterFunction))
+ m_stdoutFilterFunction = QLatin1Char('(') + getJsString(ctx, stdoutFilterFunction) + QLatin1Char(')');
+
+ const ScopedJsValue stderrFilterFunction(ctx, getJsProperty(ctx, *scriptValue,
+ stderrFilterFunctionProperty()));
+ if (JS_IsFunction(ctx, stderrFilterFunction))
+ m_stderrFilterFunction = QLatin1Char('(') + getJsString(ctx, stderrFilterFunction) + QLatin1Char(')');
+
+ m_relevantEnvVars = getJsStringListProperty(ctx, *scriptValue,
+ QStringLiteral("relevantEnvironmentVariables"));
+ m_responseFileThreshold = getJsIntProperty(ctx, *scriptValue, responseFileThresholdProperty());
+ m_responseFileArgumentIndex = getJsIntProperty(ctx, *scriptValue,
+ responseFileArgumentIndexProperty());
+ m_responseFileUsagePrefix = getJsStringProperty(ctx, *scriptValue,
+ responseFileUsagePrefixProperty());
+ m_responseFileSeparator = getJsStringProperty(ctx, *scriptValue,
+ responseFileSeparatorProperty());
+ QStringList envList = getJsStringListProperty(ctx, *scriptValue, environmentProperty());
getEnvironmentFromList(envList);
- m_stdoutFilePath = scriptValue->property(stdoutFilePathProperty()).toString();
- m_stderrFilePath = scriptValue->property(stderrFilePathProperty()).toString();
+ m_stdoutFilePath = getJsStringProperty(ctx, *scriptValue, stdoutFilePathProperty());
+ m_stderrFilePath = getJsStringProperty(ctx, *scriptValue, stderrFilePathProperty());
m_predefinedProperties
<< programProperty()
@@ -337,7 +336,7 @@ void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue, const
<< environmentProperty()
<< stdoutFilePathProperty()
<< stderrFilePathProperty();
- applyCommandProperties(scriptValue);
+ applyCommandProperties(ctx, scriptValue);
}
QStringList ProcessCommand::relevantEnvVars() const
@@ -365,43 +364,42 @@ void ProcessCommand::store(PersistentPool &pool)
serializationOp<PersistentPool::Store>(pool);
}
-static QString currentImportScopeName(QScriptContext *context)
+static QString currentImportScopeName(JSContext *ctx)
{
- for (; context; context = context->parentContext()) {
- QScriptValue v = context->thisObject()
- .property(StringConstants::importScopeNamePropertyInternal());
- if (v.isString())
- return v.toString();
+ const ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const JSValueList &contextStack = engine->contextStack();
+ for (auto it = contextStack.rbegin(); it != contextStack.rend(); ++it) {
+ if (!JS_IsObject(*it))
+ continue;
+ const ScopedJsValue v(ctx, getJsProperty(ctx, *it,
+ StringConstants::importScopeNamePropertyInternal()));
+ if (JS_IsString(v))
+ return getJsString(ctx, v);
}
return {};
}
-static QScriptValue js_JavaScriptCommand(QScriptContext *context, QScriptEngine *engine)
+static JSValue js_JavaScriptCommand(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *, int)
{
- if (Q_UNLIKELY(!context->isCalledAsConstructor()))
- return context->throwError(Tr::tr("JavaScriptCommand constructor called without new."));
- if (Q_UNLIKELY(context->argumentCount() != 0)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("JavaScriptCommand c'tor doesn't take arguments."));
- }
+ if (argc > 0)
+ return throwError(ctx, Tr::tr("JavaScriptCommand c'tor doesn't take arguments."));
static JavaScriptCommandPtr commandPrototype = JavaScriptCommand::create();
- QScriptValue cmd = js_CommandBase(context, engine);
- cmd.setProperty(StringConstants::classNameProperty(),
- engine->toScriptValue(StringConstants::javaScriptCommandType()));
- cmd.setProperty(StringConstants::sourceCodeProperty(),
- engine->toScriptValue(commandPrototype->sourceCode()));
- cmd.setProperty(StringConstants::importScopeNamePropertyInternal(),
- engine->toScriptValue(currentImportScopeName(context)));
-
+ JSValue cmd = js_CommandBase(ctx);
+ setJsProperty(ctx, cmd, StringConstants::classNameProperty(),
+ makeJsString(ctx, StringConstants::javaScriptCommandType()));
+ setJsProperty(ctx, cmd, StringConstants::sourceCodeProperty(),
+ makeJsString(ctx, commandPrototype->sourceCode()));
+ setJsProperty(ctx, cmd, StringConstants::importScopeNamePropertyInternal(),
+ makeJsString(ctx, currentImportScopeName(ctx)));
return cmd;
}
-void JavaScriptCommand::setupForJavaScript(QScriptValue targetObject)
+void JavaScriptCommand::setupForJavaScript(ScriptEngine *engine, JSValue targetObject)
{
- QBS_CHECK(targetObject.isObject());
- QScriptValue ctor = targetObject.engine()->newFunction(js_JavaScriptCommand, 0);
- targetObject.setProperty(StringConstants::javaScriptCommandType(), ctor);
+ engine->registerClass(StringConstants::javaScriptCommandType().toUtf8().constData(),
+ js_JavaScriptCommand, nullptr, targetObject);
}
JavaScriptCommand::JavaScriptCommand() = default;
@@ -414,22 +412,24 @@ bool JavaScriptCommand::equals(const AbstractCommand *otherAbstractCommand) cons
return m_sourceCode == other->m_sourceCode;
}
-void JavaScriptCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
+void JavaScriptCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
+ const CodeLocation &codeLocation)
{
- AbstractCommand::fillFromScriptValue(scriptValue, codeLocation);
+ AbstractCommand::fillFromScriptValue(ctx, scriptValue, codeLocation);
- const QScriptValue importScope = scriptValue->property(
- StringConstants::importScopeNamePropertyInternal());
- if (importScope.isString())
- m_scopeName = importScope.toString();
+ const ScopedJsValue importScope(ctx, getJsProperty(ctx, *scriptValue,
+ StringConstants::importScopeNamePropertyInternal()));
+ if (JS_IsString(importScope))
+ m_scopeName = getJsString(ctx, importScope);
- const QScriptValue sourceCode = scriptValue->property(StringConstants::sourceCodeProperty());
- m_sourceCode = invokedSourceCode(sourceCode);
+ const ScopedJsValue sourceCode(ctx, getJsProperty(ctx, *scriptValue,
+ StringConstants::sourceCodeProperty()));
+ m_sourceCode = invokedSourceCode(ctx, sourceCode);
m_predefinedProperties << StringConstants::classNameProperty()
<< StringConstants::sourceCodeProperty()
<< StringConstants::importScopeNamePropertyInternal();
- applyCommandProperties(scriptValue);
+ applyCommandProperties(ctx, scriptValue);
}
void JavaScriptCommand::load(PersistentPool &pool)
diff --git a/src/lib/corelib/buildgraph/rulecommands.h b/src/lib/corelib/buildgraph/rulecommands.h
index 725cd6d89..7b08d1015 100644
--- a/src/lib/corelib/buildgraph/rulecommands.h
+++ b/src/lib/corelib/buildgraph/rulecommands.h
@@ -47,14 +47,15 @@
#include <tools/persistence.h>
#include <tools/set.h>
+#include <quickjs.h>
+
#include <QtCore/qprocess.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
+class ScriptEngine;
class AbstractCommand
{
@@ -75,7 +76,8 @@ public:
virtual CommandType type() const = 0;
virtual bool equals(const AbstractCommand *other) const;
- virtual void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation);
+ virtual void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
+ const CodeLocation &codeLocation);
QString fullDescription(const QString &productName) const;
const QString description() const { return m_description; }
@@ -94,7 +96,7 @@ public:
protected:
AbstractCommand();
- void applyCommandProperties(const QScriptValue *scriptValue);
+ void applyCommandProperties(JSContext *ctx, const JSValue *scriptValue);
Set<QString> m_predefinedProperties;
@@ -121,11 +123,11 @@ class ProcessCommand : public AbstractCommand
{
public:
static ProcessCommandPtr create() { return ProcessCommandPtr(new ProcessCommand); }
- static void setupForJavaScript(QScriptValue targetObject);
+ static void setupForJavaScript(ScriptEngine *engine, JSValue targetObject);
CommandType type() const override { return ProcessCommandType; }
bool equals(const AbstractCommand *otherAbstractCommand) const override;
- void fillFromScriptValue(const QScriptValue *scriptValue,
+ void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
const CodeLocation &codeLocation) override;
const QString program() const { return m_program; }
const QStringList arguments() const { return m_arguments; }
@@ -186,16 +188,15 @@ class JavaScriptCommand : public AbstractCommand
{
public:
static JavaScriptCommandPtr create() { return JavaScriptCommandPtr(new JavaScriptCommand); }
- static void setupForJavaScript(QScriptValue targetObject);
+ static void setupForJavaScript(ScriptEngine *engine, JSValue targetObject);
CommandType type() const override { return JavaScriptCommandType; }
bool equals(const AbstractCommand *otherAbstractCommand) const override;
- void fillFromScriptValue(const QScriptValue *scriptValue,
+ void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
const CodeLocation &codeLocation) override;
const QString &scopeName() const { return m_scopeName; }
const QString &sourceCode() const { return m_sourceCode; }
- void setSourceCode(const QString &str) { m_sourceCode = str; }
void load(PersistentPool &pool) override;
void store(PersistentPool &pool) override;
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp
index f464734b8..84956d123 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.cpp
+++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp
@@ -1,5 +1,3 @@
-#include <utility>
-
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -41,7 +39,6 @@
#include "rulesapplicator.h"
#include "buildgraph.h"
-#include "productbuilddata.h"
#include "projectbuilddata.h"
#include "qtmocscanner.h"
#include "rulecommands.h"
@@ -57,7 +54,6 @@
#include <language/preparescriptobserver.h>
#include <language/propertymapinternal.h>
#include <language/resolvedfilecontext.h>
-#include <language/scriptengine.h>
#include <logging/categories.h>
#include <logging/translator.h>
#include <tools/error.h>
@@ -69,7 +65,6 @@
#include <QtCore/qcryptographichash.h>
#include <QtCore/qdir.h>
-#include <QtScript/qscriptvalueiterator.h>
#include <memory>
#include <vector>
@@ -113,10 +108,10 @@ void RulesApplicator::applyRule(RuleNode *ruleNode, const ArtifactSet &inputArti
m_completeInputSet = inputArtifacts;
if (m_rule->name.startsWith(QLatin1String("QtCoreMocRule"))) {
delete m_mocScanner;
- m_mocScanner = new QtMocScanner(m_product, scope());
+ m_mocScanner = new QtMocScanner(m_product, engine(), scope());
}
- QScriptValue prepareScriptContext = engine()->newObject();
- prepareScriptContext.setPrototype(engine()->globalObject());
+ ScopedJsValue prepareScriptContext(jsContext(), engine()->newObject());
+ JS_SetPrototype(jsContext(), prepareScriptContext, engine()->globalObject());
setupScriptEngineForFile(engine(), m_rule->prepareScript.fileContext(), scope(),
ObserveMode::Enabled);
setupScriptEngineForProduct(engine(), m_product.get(), m_rule->module.get(),
@@ -134,6 +129,7 @@ void RulesApplicator::applyRule(RuleNode *ruleNode, const ArtifactSet &inputArti
}
if (engine()->usesIo())
m_ruleUsesIo = true;
+ engine()->releaseInputArtifactScriptValues(ruleNode);
}
void RulesApplicator::handleRemovedRuleOutputs(const ArtifactSet &inputArtifacts,
@@ -164,9 +160,9 @@ ArtifactSet RulesApplicator::collectAuxiliaryInputs(const Rule *rule,
CurrentProduct | Dependencies);
}
-static void copyProperty(const QString &name, const QScriptValue &src, QScriptValue dst)
+static void copyProperty(JSContext *ctx, const QString &name, const JSValue &src, JSValue dst)
{
- dst.setProperty(name, src.property(name));
+ setJsProperty(ctx, dst, name, getJsProperty(ctx, src, name));
}
static QStringList toStringList(const ArtifactSet &artifacts)
@@ -180,7 +176,7 @@ static QStringList toStringList(const ArtifactSet &artifacts)
return lst;
}
-void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext)
+void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, JSValue prepareScriptContext)
{
evalContext()->checkForCancelation();
for (const Artifact *inputArtifact : inputArtifacts)
@@ -202,20 +198,22 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p
engine()->clearRequestedProperties();
// create the output artifacts from the set of input artifacts
- m_transformer->setupInputs(prepareScriptContext);
- m_transformer->setupExplicitlyDependsOn(prepareScriptContext);
- copyProperty(StringConstants::inputsVar(), prepareScriptContext, scope());
- copyProperty(StringConstants::inputVar(), prepareScriptContext, scope());
- copyProperty(StringConstants::explicitlyDependsOnVar(), prepareScriptContext, scope());
- copyProperty(StringConstants::productVar(), prepareScriptContext, scope());
- copyProperty(StringConstants::projectVar(), prepareScriptContext, scope());
+ m_transformer->setupInputs(engine(), prepareScriptContext);
+ m_transformer->setupExplicitlyDependsOn(engine(), prepareScriptContext);
+ copyProperty(jsContext(), StringConstants::inputsVar(), prepareScriptContext, scope());
+ copyProperty(jsContext(), StringConstants::inputVar(), prepareScriptContext, scope());
+ copyProperty(jsContext(), StringConstants::explicitlyDependsOnVar(),
+ prepareScriptContext, scope());
+ copyProperty(jsContext(), StringConstants::productVar(), prepareScriptContext, scope());
+ copyProperty(jsContext(), StringConstants::projectVar(), prepareScriptContext, scope());
if (m_rule->isDynamic()) {
- outputArtifacts = runOutputArtifactsScript(inputArtifacts,
- ScriptEngine::argumentList(Rule::argumentNamesForOutputArtifacts(), scope()));
+ const ScopedJsValueList argList
+ = engine()->argumentList(Rule::argumentNamesForOutputArtifacts(), scope());
+ outputArtifacts = runOutputArtifactsScript(inputArtifacts, argList);
} else {
Set<QString> outputFilePaths;
for (const auto &ruleArtifact : m_rule->artifacts) {
- const OutputArtifactInfo outputInfo = createOutputArtifactFromRuleArtifact(
+ const OutputArtifactInfo &outputInfo = createOutputArtifactFromRuleArtifact(
ruleArtifact, inputArtifacts, &outputFilePaths);
if (!outputInfo.artifact)
continue;
@@ -255,53 +253,57 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p
}
if (inputArtifacts != m_transformer->inputs)
- m_transformer->setupInputs(prepareScriptContext);
+ m_transformer->setupInputs(engine(), prepareScriptContext);
// change the transformer outputs according to the bindings in Artifact
- QScriptValue scriptValue;
- if (!ruleArtifactArtifactMap.empty())
- engine()->setGlobalObject(prepareScriptContext);
- for (auto it = ruleArtifactArtifactMap.crbegin(), end = ruleArtifactArtifactMap.crend();
- it != end; ++it) {
- const RuleArtifact *ra = it->first;
- if (ra->bindings.empty())
- continue;
-
- // expose attributes of this artifact
- const OutputArtifactInfo outputInfo = it->second;
- Artifact *outputArtifact = outputInfo.artifact;
- outputArtifact->properties = outputArtifact->properties->clone();
+ if (!ruleArtifactArtifactMap.empty()) {
+ const TemporaryGlobalObjectSetter gos(engine(), prepareScriptContext);
+ for (auto it = ruleArtifactArtifactMap.crbegin(), end = ruleArtifactArtifactMap.crend();
+ it != end; ++it) {
+ const RuleArtifact *ra = it->first;
+ if (ra->bindings.empty())
+ continue;
- scope().setProperty(StringConstants::fileNameProperty(),
- engine()->toScriptValue(outputArtifact->filePath()));
- scope().setProperty(StringConstants::fileTagsProperty(),
- toScriptValue(engine(), outputArtifact->fileTags().toStringList()));
-
- QVariantMap artifactModulesCfg = outputArtifact->properties->value();
- for (const auto &binding : ra->bindings) {
- scriptValue = engine()->evaluate(binding.code);
- if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue))) {
- QString msg = QStringLiteral("evaluating rule binding '%1': %2");
- throw ErrorInfo(msg.arg(binding.name.join(QLatin1Char('.')),
- engine()->lastErrorString(scriptValue)),
- engine()->lastErrorLocation(scriptValue, binding.location));
+ // expose attributes of this artifact
+ const OutputArtifactInfo &outputInfo = it->second;
+ Artifact *outputArtifact = outputInfo.artifact;
+ outputArtifact->properties = outputArtifact->properties->clone();
+
+ setJsProperty(jsContext(), scope(), StringConstants::fileNameProperty(),
+ engine()->toScriptValue(outputArtifact->filePath()));
+ setJsProperty(jsContext(), scope(), StringConstants::fileTagsProperty(),
+ makeJsStringList(engine()->context(),
+ outputArtifact->fileTags().toStringList()));
+
+ QVariantMap artifactModulesCfg = outputArtifact->properties->value();
+ for (const auto &binding : ra->bindings) {
+ const ScopedJsValue scriptValue(jsContext(), engine()->evaluate(
+ JsValueOwner::Caller, binding.code,
+ binding.location.filePath(),
+ binding.location.line()));
+ if (JsException ex = engine()->checkAndClearException(binding.location)) {
+ ErrorInfo err = ex.toErrorInfo();
+ err.prepend(QStringLiteral("evaluating rule binding '%1'")
+ .arg(binding.name.join(QLatin1Char('.'))));
+ throw err;
+ }
+ const QVariant value = getJsVariant(jsContext(), scriptValue);
+ setConfigProperty(artifactModulesCfg, binding.name, value);
+ outputArtifact->pureProperties.emplace_back(binding.name, value);
+ }
+ outputArtifact->properties->setValue(artifactModulesCfg);
+ if (!outputInfo.newlyCreated
+ && (outputArtifact->fileTags() != outputInfo.oldFileTags
+ || outputArtifact->properties->value() != outputInfo.oldProperties)) {
+ invalidateArtifactAsRuleInputIfNecessary(outputArtifact);
}
- const QVariant value = scriptValue.toVariant();
- setConfigProperty(artifactModulesCfg, binding.name, value);
- outputArtifact->pureProperties.emplace_back(binding.name, value);
- }
- outputArtifact->properties->setValue(artifactModulesCfg);
- if (!outputInfo.newlyCreated && (outputArtifact->fileTags() != outputInfo.oldFileTags
- || outputArtifact->properties->value() != outputInfo.oldProperties)) {
- invalidateArtifactAsRuleInputIfNecessary(outputArtifact);
}
}
- if (!ruleArtifactArtifactMap.empty())
- engine()->setGlobalObject(prepareScriptContext.prototype());
- m_transformer->setupOutputs(prepareScriptContext);
- m_transformer->createCommands(engine(), m_rule->prepareScript,
- ScriptEngine::argumentList(Rule::argumentNamesForPrepare(), prepareScriptContext));
+ m_transformer->setupOutputs(engine(), prepareScriptContext);
+ const ScopedJsValueList argList = engine()->argumentList(Rule::argumentNamesForPrepare(),
+ prepareScriptContext);
+ m_transformer->createCommands(engine(), m_rule->prepareScript, argList);
if (Q_UNLIKELY(m_transformer->commands.empty()))
throw ErrorInfo(Tr::tr("There is a rule without commands: %1.")
.arg(m_rule->toString()), m_rule->prepareScript.location());
@@ -386,12 +388,14 @@ RulesApplicator::OutputArtifactInfo RulesApplicator::createOutputArtifactFromRul
FileTags fileTags;
bool alwaysUpdated;
if (ruleArtifact) {
- QScriptValue scriptValue = engine()->evaluate(ruleArtifact->filePath,
- ruleArtifact->filePathLocation.filePath(),
- ruleArtifact->filePathLocation.line());
- if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue)))
- throw engine()->lastError(scriptValue, ruleArtifact->filePathLocation);
- outputPath = scriptValue.toString();
+ const ScopedJsValue scriptValue(
+ jsContext(),
+ engine()->evaluate(JsValueOwner::Caller, ruleArtifact->filePath,
+ ruleArtifact->filePathLocation.filePath(),
+ ruleArtifact->filePathLocation.line()));
+ if (JsException ex = engine()->checkAndClearException(ruleArtifact->filePathLocation))
+ throw ex.toErrorInfo();
+ outputPath = getJsString(jsContext(), scriptValue);
fileTags = ruleArtifact->fileTags;
alwaysUpdated = ruleArtifact->alwaysUpdated;
} else {
@@ -514,26 +518,31 @@ public:
};
QList<Artifact *> RulesApplicator::runOutputArtifactsScript(const ArtifactSet &inputArtifacts,
- const QScriptValueList &args)
+ const JSValueList &args)
{
QList<Artifact *> lst;
- QScriptValue fun = engine()->evaluate(m_rule->outputArtifactsScript.sourceCode(),
- m_rule->outputArtifactsScript.location().filePath(),
- m_rule->outputArtifactsScript.location().line());
- if (!fun.isFunction())
+ const ScopedJsValue fun(jsContext(),
+ engine()->evaluate(JsValueOwner::Caller,
+ m_rule->outputArtifactsScript.sourceCode(),
+ m_rule->outputArtifactsScript.location().filePath(),
+ m_rule->outputArtifactsScript.location().line()));
+ if (!JS_IsFunction(jsContext(), fun))
throw ErrorInfo(QStringLiteral("Function expected."),
m_rule->outputArtifactsScript.location());
- QScriptValue res = fun.call(QScriptValue(), args);
- engine()->releaseResourcesOfScriptObjects();
- if (engine()->hasErrorOrException(res))
- throw engine()->lastError(res, m_rule->outputArtifactsScript.location());
- if (!res.isArray())
+ JSValueList argv(args.begin(), args.end());
+ const ScopedJsValue res(
+ jsContext(),
+ JS_Call(jsContext(), fun, engine()->globalObject(), int(args.size()), argv.data()));
+ if (JsException ex = engine()->checkAndClearException(m_rule->outputArtifactsScript.location()))
+ throw ex.toErrorInfo();
+ if (!JS_IsArray(jsContext(), res))
throw ErrorInfo(Tr::tr("Rule.outputArtifacts must return an array of objects."),
m_rule->outputArtifactsScript.location());
- const quint32 c = res.property(StringConstants::lengthProperty()).toUInt32();
+ const quint32 c = getJsIntProperty(jsContext(), res, StringConstants::lengthProperty());
for (quint32 i = 0; i < c; ++i) {
try {
- lst.push_back(createOutputArtifactFromScriptValue(res.property(i), inputArtifacts));
+ ScopedJsValue val(engine()->context(), JS_GetPropertyUint32(jsContext(), res, i));
+ lst.push_back(createOutputArtifactFromScriptValue(val, inputArtifacts));
} catch (const RuleOutputArtifactsException &roae) {
ErrorInfo ei = roae;
ei.prepend(Tr::tr("Error in Rule.outputArtifacts[%1]").arg(i),
@@ -556,6 +565,8 @@ class ArtifactBindingsExtractor
QString name;
QVariant value;
};
+ ScriptEngine *m_engine = nullptr;
+ JSContext *m_ctx = nullptr;
std::vector<Entry> m_propertyValues;
static Set<QString> getArtifactItemPropertyNames()
@@ -570,35 +581,36 @@ class ArtifactBindingsExtractor
return s;
}
- void extractPropertyValues(const QScriptValue &obj, const QString &moduleName = QString())
+ void extractPropertyValues(const JSValue &obj, const QString &moduleName = QString())
{
- QScriptValueIterator svit(obj);
- while (svit.hasNext()) {
- svit.next();
- const QString name = svit.name();
+ handleJsProperties(m_ctx, obj, [&](const JSAtom &prop, const JSPropertyDescriptor &desc) {
+ const QString name = getJsString(m_ctx, prop);
if (moduleName.isEmpty()) {
// Ignore property names that are part of the Artifact item.
static const Set<QString> artifactItemPropertyNames
= getArtifactItemPropertyNames();
if (artifactItemPropertyNames.contains(name))
- continue;
+ return;
}
- const QScriptValue value = svit.value();
- if (value.isObject() && !value.isArray() && !value.isError() && !value.isRegExp()) {
+ const JSValue value = desc.value;
+ if (JS_IsObject(value) && !JS_IsArray(m_ctx, value) && !JS_IsError(m_ctx, value)
+ && !JS_IsRegExp(m_ctx, value)) {
QString newModuleName;
if (!moduleName.isEmpty())
newModuleName.append(moduleName + QLatin1Char('.'));
newModuleName.append(name);
extractPropertyValues(value, newModuleName);
} else {
- m_propertyValues.emplace_back(moduleName, name, value.toVariant());
+ m_propertyValues.emplace_back(moduleName, name, getJsVariant(m_ctx, value));
}
- }
+ });
}
public:
- void apply(Artifact *outputArtifact, const QScriptValue &obj)
+ void apply(ScriptEngine *engine, Artifact *outputArtifact, const JSValue &obj)
{
+ m_engine = engine;
+ m_ctx = m_engine->context();
extractPropertyValues(obj);
if (m_propertyValues.empty())
return;
@@ -614,24 +626,27 @@ public:
}
};
-Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValue &obj,
+Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const JSValue &obj,
const ArtifactSet &inputArtifacts)
{
- if (!obj.isObject()) {
+ if (!JS_IsObject(obj)) {
throw ErrorInfo(Tr::tr("Elements of the Rule.outputArtifacts array must be "
"of Object type."), m_rule->outputArtifactsScript.location());
}
- const QString unresolvedFilePath
- = obj.property(StringConstants::filePathProperty()).toVariant().toString();
+ QString unresolvedFilePath;
+ const ScopedJsValue jsFilePath(jsContext(), getJsProperty(jsContext(), obj,
+ StringConstants::filePathProperty()));
+ if (JS_IsString(jsFilePath))
+ unresolvedFilePath = getJsString(jsContext(), jsFilePath);
if (unresolvedFilePath.isEmpty()) {
throw RuleOutputArtifactsException(
Tr::tr("Property filePath must be a non-empty string."));
}
const QString filePath = FileInfo::resolvePath(m_product->buildDirectory(), unresolvedFilePath);
const FileTags fileTags = FileTags::fromStringList(
- obj.property(StringConstants::fileTagsProperty()).toVariant().toStringList());
- const QVariant alwaysUpdatedVar
- = obj.property(StringConstants::alwaysUpdatedProperty()).toVariant();
+ getJsStringListProperty(jsContext(), obj, StringConstants::fileTagsProperty()));
+ const QVariant alwaysUpdatedVar = getJsVariantProperty(jsContext(), obj,
+ StringConstants::alwaysUpdatedProperty());
const bool alwaysUpdated = alwaysUpdatedVar.isValid() ? alwaysUpdatedVar.toBool() : true;
OutputArtifactInfo outputInfo = createOutputArtifact(filePath, fileTags, alwaysUpdated,
inputArtifacts);
@@ -642,14 +657,13 @@ Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValu
"Alternatively, a FileTagger can be provided.")
.arg(unresolvedFilePath));
}
- const FileTags explicitlyDependsOn = FileTags::fromStringList(
- obj.property(StringConstants::explicitlyDependsOnProperty())
- .toVariant().toStringList());
+ const FileTags explicitlyDependsOn = FileTags::fromStringList(getJsStringListProperty(
+ jsContext(), obj, StringConstants::explicitlyDependsOnProperty()));
for (const FileTag &tag : explicitlyDependsOn) {
for (Artifact * const dependency : m_product->lookupArtifactsByFileTag(tag))
connect(outputInfo.artifact, dependency);
}
- ArtifactBindingsExtractor().apply(outputInfo.artifact, obj);
+ ArtifactBindingsExtractor().apply(engine(), outputInfo.artifact, obj);
if (!outputInfo.newlyCreated && (outputInfo.artifact->fileTags() != outputInfo.oldFileTags
|| outputInfo.artifact->properties->value() != outputInfo.oldProperties)) {
invalidateArtifactAsRuleInputIfNecessary(outputInfo.artifact);
@@ -670,15 +684,9 @@ const RulesEvaluationContextPtr &RulesApplicator::evalContext() const
return m_product->topLevelProject()->buildData->evaluationContext;
}
-ScriptEngine *RulesApplicator::engine() const
-{
- return evalContext()->engine();
-}
-
-QScriptValue RulesApplicator::scope() const
-{
- return evalContext()->scope();
-}
+ScriptEngine *RulesApplicator::engine() const { return evalContext()->engine(); }
+JSContext *RulesApplicator::jsContext() const { return engine()->context(); }
+JSValue RulesApplicator::scope() const { return evalContext()->scope(); }
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.h b/src/lib/corelib/buildgraph/rulesapplicator.h
index da7815014..72f9aadcc 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.h
+++ b/src/lib/corelib/buildgraph/rulesapplicator.h
@@ -44,12 +44,13 @@
#include "nodeset.h"
#include <language/filetags.h>
#include <language/forward_decls.h>
+#include <language/scriptengine.h>
#include <logging/logger.h>
+#include <quickjs.h>
#include <QtCore/qflags.h>
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptvalue.h>
#include <unordered_map>
@@ -85,7 +86,7 @@ public:
Q_DECLARE_FLAGS(InputsSources, InputsSourceFlag)
private:
- void doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext);
+ void doApply(const ArtifactSet &inputArtifacts, JSValue prepareScriptContext);
ArtifactSet collectOldOutputArtifacts(const ArtifactSet &inputArtifacts) const;
struct OutputArtifactInfo {
@@ -100,13 +101,14 @@ private:
OutputArtifactInfo createOutputArtifact(const QString &filePath, const FileTags &fileTags,
bool alwaysUpdated, const ArtifactSet &inputArtifacts);
QList<Artifact *> runOutputArtifactsScript(const ArtifactSet &inputArtifacts,
- const QScriptValueList &args);
- Artifact *createOutputArtifactFromScriptValue(const QScriptValue &obj,
+ const JSValueList &args);
+ Artifact *createOutputArtifactFromScriptValue(const JSValue &obj,
const ArtifactSet &inputArtifacts);
QString resolveOutPath(const QString &path) const;
const RulesEvaluationContextPtr &evalContext() const;
ScriptEngine *engine() const;
- QScriptValue scope() const;
+ JSContext *jsContext() const;
+ JSValue scope() const;
static ArtifactSet collectAdditionalInputs(const FileTags &tags,
const Rule *rule, const ResolvedProduct *product,
diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp
index 0422b9288..2d0f5d44d 100644
--- a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp
+++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp
@@ -38,9 +38,7 @@
****************************************************************************/
#include "rulesevaluationcontext.h"
-#include "artifact.h"
#include "rulecommands.h"
-#include "transformer.h"
#include <language/language.h>
#include <language/scriptengine.h>
#include <logging/translator.h>
@@ -57,15 +55,19 @@ RulesEvaluationContext::RulesEvaluationContext(Logger logger)
: m_logger(std::move(logger)),
m_engine(ScriptEngine::create(m_logger, EvalContext::RuleExecution)),
m_observer(nullptr),
- m_initScopeCalls(0)
+ m_initScopeCalls(0),
+ m_prepareScriptScope(m_engine->newObject())
{
- m_prepareScriptScope = m_engine->newObject();
- m_prepareScriptScope.setPrototype(m_engine->globalObject());
- ProcessCommand::setupForJavaScript(m_prepareScriptScope);
- JavaScriptCommand::setupForJavaScript(m_prepareScriptScope);
+
+ JS_SetPrototype(m_engine->context(), m_prepareScriptScope, m_engine->globalObject());
+ ProcessCommand::setupForJavaScript(m_engine.get(), m_prepareScriptScope);
+ JavaScriptCommand::setupForJavaScript(m_engine.get(), m_prepareScriptScope);
}
-RulesEvaluationContext::~RulesEvaluationContext() = default;
+RulesEvaluationContext::~RulesEvaluationContext()
+{
+ JS_FreeValue(m_engine->context(), m_prepareScriptScope);
+}
void RulesEvaluationContext::initializeObserver(const QString &description, int maximumProgress)
{
@@ -92,7 +94,7 @@ void RulesEvaluationContext::initScope()
m_engine->setActive(true);
m_scope = m_engine->newObject();
- m_scope.setPrototype(m_prepareScriptScope);
+ JS_SetPrototype(m_engine->context(), m_scope, m_prepareScriptScope);
m_engine->setGlobalObject(m_scope);
}
@@ -102,8 +104,11 @@ void RulesEvaluationContext::cleanupScope()
if (--m_initScopeCalls > 0)
return;
- m_scope = QScriptValue();
- m_engine->setGlobalObject(m_prepareScriptScope.prototype());
+ JS_FreeValue(m_engine->context(), m_scope);
+ m_scope = JS_UNDEFINED;
+ const ScopedJsValue proto(engine()->context(),
+ JS_GetPrototype(m_engine->context(), m_prepareScriptScope));
+ m_engine->setGlobalObject(proto);
m_engine->setActive(false);
}
diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.h b/src/lib/corelib/buildgraph/rulesevaluationcontext.h
index bb7955496..7ff75b51f 100644
--- a/src/lib/corelib/buildgraph/rulesevaluationcontext.h
+++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.h
@@ -42,12 +42,11 @@
#include <language/forward_decls.h>
#include <logging/logger.h>
+#include <quickjs.h>
+
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptprogram.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
class ProgressObserver;
@@ -70,7 +69,7 @@ public:
};
ScriptEngine *engine() const { return m_engine.get(); }
- QScriptValue scope() const { return m_scope; }
+ JSValue scope() const { return m_scope; }
void setObserver(ProgressObserver *observer) { m_observer = observer; }
ProgressObserver *observer() const { return m_observer; }
@@ -79,8 +78,6 @@ public:
void checkForCancelation();
private:
- friend class Scope;
-
void initScope();
void cleanupScope();
@@ -88,8 +85,8 @@ private:
const std::unique_ptr<ScriptEngine> m_engine;
ProgressObserver *m_observer;
unsigned int m_initScopeCalls;
- QScriptValue m_scope;
- QScriptValue m_prepareScriptScope;
+ JSValue m_scope = JS_UNDEFINED;
+ const JSValue m_prepareScriptScope;
};
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h b/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h
deleted file mode 100644
index b57072236..000000000
--- a/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QBS_SCRIPTCLASSPROPERTYITERATOR_H
-#define QBS_SCRIPTCLASSPROPERTYITERATOR_H
-
-#include <tools/qbsassert.h>
-
-#include <QtCore/qmap.h>
-#include <QtCore/qstring.h>
-#include <QtScript/qscriptclasspropertyiterator.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptstring.h>
-
-#include <vector>
-
-namespace qbs {
-namespace Internal {
-
-class ScriptClassPropertyIterator : public QScriptClassPropertyIterator
-{
-public:
- ScriptClassPropertyIterator(const QScriptValue &object, const QVariantMap &properties,
- std::vector<QString> additionalProperties)
- : QScriptClassPropertyIterator(object),
- m_it(properties),
- m_additionalProperties(std::move(additionalProperties))
- {
- }
-
-private:
- bool hasNext() const override
- {
- return m_it.hasNext() || m_index < int(m_additionalProperties.size()) - 1;
- }
- bool hasPrevious() const override { return m_index > -1 || m_it.hasPrevious(); }
- void toFront() override { m_it.toFront(); m_index = -1; }
- void toBack() override { m_it.toBack(); m_index = int(m_additionalProperties.size()) - 1; }
-
- void next() override
- {
- QBS_ASSERT(hasNext(), return);
- if (m_it.hasNext())
- m_it.next();
- else
- ++m_index;
- }
-
- void previous() override
- {
- QBS_ASSERT(hasPrevious(), return);
- if (m_index >= 0)
- --m_index;
- if (m_index == -1)
- m_it.previous();
- }
-
- QScriptString name() const override
- {
- const QString theName = m_index >= 0 && m_index < int(m_additionalProperties.size())
- ? m_additionalProperties.at(m_index)
- : m_it.key();
- return object().engine()->toStringHandle(theName);
- }
-
- QMapIterator<QString, QVariant> m_it;
- const std::vector<QString> m_additionalProperties;
- int m_index = -1;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_SCRIPTCLASSPROPERTYITERATOR_H
diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp
index 29f2bcdf0..ae044cd2c 100644
--- a/src/lib/corelib/buildgraph/transformer.cpp
+++ b/src/lib/corelib/buildgraph/transformer.cpp
@@ -39,6 +39,7 @@
#include "transformer.h"
#include "artifact.h"
+#include "productbuilddata.h"
#include <jsextensions/moduleproperties.h>
#include <language/language.h>
#include <language/preparescriptobserver.h>
@@ -54,6 +55,7 @@
#include <QtCore/qdir.h>
#include <algorithm>
+#include <vector>
namespace qbs {
namespace Internal {
@@ -64,78 +66,82 @@ Transformer::Transformer() : alwaysRun(false)
Transformer::~Transformer() = default;
-static QScriptValue js_baseName(QScriptContext *ctx, QScriptEngine *engine,
- const Artifact *artifact)
+static JSValue js_baseName(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- Q_UNUSED(ctx);
- Q_UNUSED(engine);
- return {FileInfo::baseName(artifact->filePath())};
+ return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val,
+ [ctx](const Artifact *a) {
+ return makeJsString(ctx, FileInfo::baseName(a->filePath()));
+ });
}
-static QScriptValue js_completeBaseName(QScriptContext *ctx, QScriptEngine *engine,
- const Artifact *artifact)
+static JSValue js_completeBaseName(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- Q_UNUSED(ctx);
- Q_UNUSED(engine);
- return {FileInfo::completeBaseName(artifact->filePath())};
+ return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val,
+ [ctx](const Artifact *a) {
+ return makeJsString(ctx, FileInfo::completeBaseName(a->filePath()));
+ });
}
-static QScriptValue js_baseDir(QScriptContext *ctx, QScriptEngine *engine,
- const Artifact *artifact)
+static JSValue js_baseDir(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- Q_UNUSED(ctx);
- Q_UNUSED(engine);
- QString basedir;
- if (artifact->artifactType == Artifact::SourceFile) {
- QDir sourceDir(artifact->product->sourceDirectory);
- basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
- } else {
- QDir buildDir(artifact->product->buildDirectory());
- basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
- }
- return basedir;
+ return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val,
+ [ctx](const Artifact *artifact) {
+ QString basedir;
+ if (artifact->artifactType == Artifact::SourceFile) {
+ QDir sourceDir(artifact->product->sourceDirectory);
+ basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
+ } else {
+ QDir buildDir(artifact->product->buildDirectory());
+ basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
+ }
+ return makeJsString(ctx, basedir);
+ });
}
-static QScriptValue js_children(QScriptContext *ctx, QScriptEngine *engine, const Artifact *artifact)
+static JSValue js_children(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- Q_UNUSED(ctx);
- QScriptValue sv = engine->newArray();
- uint idx = 0;
- for (const Artifact *child : artifact->childArtifacts()) {
- sv.setProperty(idx++, Transformer::translateFileConfig(static_cast<ScriptEngine *>(engine),
- child, QString()));
- }
- return sv;
+ return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val,
+ [ctx](const Artifact *artifact) {
+ JSValue sv = JS_NewArray(ctx);
+ uint idx = 0;
+
+ // FIXME: childArtifacts() is not guarded by any mutex ...
+ for (Artifact *child : artifact->childArtifacts()) {
+ JS_SetPropertyUint32(ctx, sv, idx++, Transformer::translateFileConfig(
+ ScriptEngine::engineForContext(ctx), child, QString()));
+ }
+ return sv;
+ });
}
-static void setArtifactProperty(QScriptValue &obj, const QString &name,
- QScriptValue (*func)(QScriptContext *, QScriptEngine *, const Artifact *),
- const Artifact *artifact)
+static void setArtifactProperty(JSContext *ctx, JSValue &obj, const QString &name,
+ JSCFunction *func)
{
- obj.setProperty(name, static_cast<ScriptEngine *>(obj.engine())->newFunction(func, artifact),
- QScriptValue::PropertyGetter);
+ const QByteArray nameBa = name.toUtf8();
+ const JSValue jsFunc = JS_NewCFunction(ctx, func, nameBa.constData(), 0);
+ const ScopedJsAtom nameAtom(ctx, nameBa);
+ JS_DefinePropertyGetSet(ctx, obj, nameAtom, jsFunc, JS_UNDEFINED, JS_PROP_HAS_GET);
}
-QScriptValue Transformer::translateFileConfig(ScriptEngine *scriptEngine, const Artifact *artifact,
- const QString &defaultModuleName)
+JSValue Transformer::translateFileConfig(ScriptEngine *engine, Artifact *artifact,
+ const QString &defaultModuleName)
{
- QScriptValue obj = scriptEngine->newObject();
- attachPointerTo(obj, artifact);
- ModuleProperties::init(obj, artifact);
- obj.setProperty(StringConstants::fileNameProperty(), artifact->fileName());
- obj.setProperty(StringConstants::filePathProperty(), artifact->filePath());
- setArtifactProperty(obj, StringConstants::baseNameProperty(), js_baseName, artifact);
- setArtifactProperty(obj, StringConstants::completeBaseNameProperty(), js_completeBaseName,
- artifact);
- setArtifactProperty(obj, QStringLiteral("baseDir"), js_baseDir, artifact);
- setArtifactProperty(obj, QStringLiteral("children"), js_children, artifact);
- const QStringList fileTags = sorted(artifact->fileTags().toStringList());
- scriptEngine->setObservedProperty(obj, StringConstants::fileTagsProperty(),
- scriptEngine->toScriptValue(fileTags));
- scriptEngine->observer()->addArtifactId(obj.objectId());
- if (!defaultModuleName.isEmpty())
- obj.setProperty(StringConstants::moduleNameProperty(), defaultModuleName);
- return obj;
+ return engine->getArtifactScriptValue(artifact, defaultModuleName, [&](JSValue obj) {
+ ModuleProperties::init(engine, obj, artifact);
+ JSContext * const ctx = engine->context();
+ setJsProperty(ctx, obj, StringConstants::fileNameProperty(), artifact->fileName());
+ setJsProperty(ctx, obj, StringConstants::filePathProperty(), artifact->filePath());
+ setArtifactProperty(ctx, obj, StringConstants::baseNameProperty(), js_baseName);
+ setArtifactProperty(ctx, obj, StringConstants::completeBaseNameProperty(), js_completeBaseName);
+ setArtifactProperty(ctx, obj, QStringLiteral("baseDir"), js_baseDir);
+ setArtifactProperty(ctx, obj, QStringLiteral("children"), js_children);
+ const QStringList fileTags = sorted(artifact->fileTags().toStringList());
+ const ScopedJsValue jsFileTags(ctx, engine->toScriptValue(fileTags));
+ engine->setObservedProperty(obj, StringConstants::fileTagsProperty(), jsFileTags);
+ engine->observer()->addArtifactId(jsObjectId(obj));
+ if (!defaultModuleName.isEmpty())
+ setJsProperty(ctx, obj, StringConstants::moduleNameProperty(), defaultModuleName);
+ });
}
static bool compareByFilePath(const Artifact *a1, const Artifact *a2)
@@ -143,9 +149,8 @@ static bool compareByFilePath(const Artifact *a1, const Artifact *a2)
return a1->filePath() < a2->filePath();
}
-QScriptValue Transformer::translateInOutputs(ScriptEngine *scriptEngine,
- const ArtifactSet &artifacts,
- const QString &defaultModuleName)
+JSValue Transformer::translateInOutputs(ScriptEngine *engine, const ArtifactSet &artifacts,
+ const QString &defaultModuleName)
{
using TagArtifactsMap = QMap<QString, QList<Artifact*>>;
TagArtifactsMap tagArtifactsMap;
@@ -155,16 +160,16 @@ QScriptValue Transformer::translateInOutputs(ScriptEngine *scriptEngine,
for (TagArtifactsMap::Iterator it = tagArtifactsMap.begin(); it != tagArtifactsMap.end(); ++it)
std::sort(it.value().begin(), it.value().end(), compareByFilePath);
- QScriptValue jsTagFiles = scriptEngine->newObject();
- for (TagArtifactsMap::const_iterator tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) {
+ JSValue jsTagFiles = engine->newObject();
+ for (auto tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) {
const QList<Artifact*> &artifacts = tag.value();
- QScriptValue jsFileConfig = scriptEngine->newArray(artifacts.size());
+ JSValue jsFileConfig = JS_NewArray(engine->context());
int i = 0;
for (Artifact * const artifact : artifacts) {
- jsFileConfig.setProperty(i++, translateFileConfig(scriptEngine, artifact,
- defaultModuleName));
+ JS_SetPropertyUint32(engine->context(), jsFileConfig, i++,
+ translateFileConfig(engine, artifact, defaultModuleName));
}
- jsTagFiles.setProperty(tag.key(), jsFileConfig);
+ setJsProperty(engine->context(), jsTagFiles, tag.key(), jsFileConfig);
}
return jsTagFiles;
@@ -177,66 +182,70 @@ ResolvedProductPtr Transformer::product() const
return (*outputs.cbegin())->product.lock();
}
-void Transformer::setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
- const QString &defaultModuleName)
+void Transformer::setupInputs(ScriptEngine *engine, JSValue targetScriptValue,
+ const ArtifactSet &inputs, const QString &defaultModuleName)
{
- const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine());
- QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName);
- targetScriptValue.setProperty(StringConstants::inputsVar(), scriptValue);
- QScriptValue inputScriptValue;
+ JSValue scriptValue = translateInOutputs(engine, inputs, defaultModuleName);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::inputsVar(), scriptValue);
+ JSValue inputScriptValue = JS_UNDEFINED;
if (inputs.size() == 1) {
Artifact *input = *inputs.cbegin();
const FileTags &fileTags = input->fileTags();
QBS_ASSERT(!fileTags.empty(), return);
- QScriptValue inputsForFileTag = scriptValue.property(fileTags.cbegin()->toString());
- inputScriptValue = inputsForFileTag.property(0);
+ const ScopedJsValue inputsForFileTag(
+ engine->context(),
+ getJsProperty(engine->context(), scriptValue, fileTags.cbegin()->toString()));
+ inputScriptValue = JS_GetPropertyUint32(engine->context(), inputsForFileTag, 0);
}
- targetScriptValue.setProperty(StringConstants::inputVar(), inputScriptValue);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::inputVar(),
+ inputScriptValue);
}
-void Transformer::setupInputs(const QScriptValue &targetScriptValue)
+void Transformer::setupInputs(ScriptEngine *engine, const JSValue &targetScriptValue)
{
- setupInputs(targetScriptValue, inputs, rule->module->name);
+ setupInputs(engine, targetScriptValue, inputs, rule->module->name);
}
-void Transformer::setupOutputs(QScriptValue targetScriptValue)
+void Transformer::setupOutputs(ScriptEngine *engine, JSValue targetScriptValue)
{
- const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine());
const QString &defaultModuleName = rule->module->name;
- QScriptValue scriptValue = translateInOutputs(scriptEngine, outputs, defaultModuleName);
- targetScriptValue.setProperty(StringConstants::outputsVar(), scriptValue);
- QScriptValue outputScriptValue;
+ JSValue scriptValue = translateInOutputs(engine, outputs, defaultModuleName);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::outputsVar(), scriptValue);
+ JSValue outputScriptValue = JS_UNDEFINED;
if (outputs.size() == 1) {
Artifact *output = *outputs.cbegin();
const FileTags &fileTags = output->fileTags();
QBS_ASSERT(!fileTags.empty(), return);
- QScriptValue outputsForFileTag = scriptValue.property(fileTags.cbegin()->toString());
- outputScriptValue = outputsForFileTag.property(0);
+ const ScopedJsValue outputsForFileTag(
+ engine->context(),
+ getJsProperty(engine->context(), scriptValue, fileTags.cbegin()->toString()));
+ outputScriptValue = JS_GetPropertyUint32(engine->context(), outputsForFileTag, 0);
}
- targetScriptValue.setProperty(StringConstants::outputVar(), outputScriptValue);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::outputVar(),
+ outputScriptValue);
}
-void Transformer::setupExplicitlyDependsOn(QScriptValue targetScriptValue)
+void Transformer::setupExplicitlyDependsOn(ScriptEngine *engine, JSValue targetScriptValue)
{
- const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine());
- QScriptValue scriptValue = translateInOutputs(scriptEngine, explicitlyDependsOn,
- rule->module->name);
- targetScriptValue.setProperty(StringConstants::explicitlyDependsOnVar(), scriptValue);
+ JSValue scriptValue = translateInOutputs(engine, explicitlyDependsOn, rule->module->name);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::explicitlyDependsOnVar(),
+ scriptValue);
}
-AbstractCommandPtr Transformer::createCommandFromScriptValue(const QScriptValue &scriptValue,
- const CodeLocation &codeLocation)
+AbstractCommandPtr Transformer::createCommandFromScriptValue(
+ ScriptEngine *engine, const JSValue &scriptValue, const CodeLocation &codeLocation)
{
AbstractCommandPtr cmdBase;
- if (scriptValue.isUndefined() || !scriptValue.isValid())
+ if (JS_IsUndefined(scriptValue) || JS_IsUninitialized(scriptValue))
return cmdBase;
- QString className = scriptValue.property(StringConstants::classNameProperty()).toString();
+ QString className = getJsStringProperty(engine->context(), scriptValue,
+ StringConstants::classNameProperty());
if (className == StringConstants::commandType())
cmdBase = ProcessCommand::create();
else if (className == StringConstants::javaScriptCommandType())
cmdBase = JavaScriptCommand::create();
if (cmdBase)
- cmdBase->fillFromScriptValue(&scriptValue, codeLocation);
+ cmdBase->fillFromScriptValue(engine->context(), &scriptValue, codeLocation);
if (className == StringConstants::commandType()) {
auto procCmd = static_cast<ProcessCommand *>(cmdBase.get());
procCmd->clearRelevantEnvValues();
@@ -248,18 +257,20 @@ AbstractCommandPtr Transformer::createCommandFromScriptValue(const QScriptValue
}
void Transformer::createCommands(ScriptEngine *engine, const PrivateScriptFunction &script,
- const QScriptValueList &args)
+ const JSValueList &args)
{
- if (!script.scriptFunction.isValid() || script.scriptFunction.engine() != engine) {
- script.scriptFunction = engine->evaluate(script.sourceCode(),
+ if (JS_IsUndefined(script.scriptFunction)) {
+ script.scriptFunction = engine->evaluate(JsValueOwner::ScriptEngine, script.sourceCode(),
script.location().filePath(),
script.location().line());
- if (Q_UNLIKELY(!script.scriptFunction.isFunction()))
+ if (Q_UNLIKELY(!JS_IsFunction(engine->context(), script.scriptFunction)))
throw ErrorInfo(Tr::tr("Invalid prepare script."), script.location());
}
-
- QScriptValue scriptValue = script.scriptFunction.call(QScriptValue(), args);
- engine->releaseResourcesOfScriptObjects();
+ JSValueList argv(args.cbegin(), args.cend());
+ const ScopedJsValue scriptValue(
+ engine->context(),
+ JS_Call(engine->context(), script.scriptFunction, engine->globalObject(),
+ int(argv.size()), argv.data()));
propertiesRequestedInPrepareScript = engine->propertiesRequestedInScript();
propertiesRequestedFromArtifactInPrepareScript = engine->propertiesRequestedFromArtifact();
importedFilesUsedInPrepareScript = engine->importedFilesUsedInScript();
@@ -271,22 +282,24 @@ void Transformer::createCommands(ScriptEngine *engine, const PrivateScriptFuncti
p->exportedModule));
}
engine->clearRequestedProperties();
- if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue)))
- throw engine->lastError(scriptValue, script.location());
+ if (JsException ex = engine->checkAndClearException(script.location()))
+ throw ex.toErrorInfo();
commands.clear();
- if (scriptValue.isArray()) {
- const int count = scriptValue.property(StringConstants::lengthProperty()).toInt32();
+ if (JS_IsArray(engine->context(), scriptValue)) {
+ const int count = JS_VALUE_GET_INT(getJsProperty(engine->context(), scriptValue,
+ StringConstants::lengthProperty()));
for (qint32 i = 0; i < count; ++i) {
- QScriptValue item = scriptValue.property(i);
- if (item.isValid() && !item.isUndefined()) {
- const AbstractCommandPtr cmd
- = createCommandFromScriptValue(item, script.location());
+ ScopedJsValue item(engine->context(),
+ JS_GetPropertyUint32(engine->context(), scriptValue, i));
+ if (!JS_IsUninitialized(item) && !JS_IsUndefined(item)) {
+ const AbstractCommandPtr cmd = createCommandFromScriptValue(engine, item,
+ script.location());
if (cmd)
commands.addCommand(cmd);
}
}
} else {
- const AbstractCommandPtr cmd = createCommandFromScriptValue(scriptValue,
+ const AbstractCommandPtr cmd = createCommandFromScriptValue(engine, scriptValue,
script.location());
if (cmd)
commands.addCommand(cmd);
diff --git a/src/lib/corelib/buildgraph/transformer.h b/src/lib/corelib/buildgraph/transformer.h
index 8772ed868..927572310 100644
--- a/src/lib/corelib/buildgraph/transformer.h
+++ b/src/lib/corelib/buildgraph/transformer.h
@@ -52,6 +52,8 @@
#include <tools/filetime.h>
#include <tools/persistence.h>
+#include <quickjs.h>
+
#include <QtCore/qhash.h>
namespace qbs {
@@ -91,15 +93,14 @@ public:
bool commandsNeedChangeTracking = false;
bool markedForRerun = false;
- static QScriptValue translateFileConfig(ScriptEngine *scriptEngine,
- const Artifact *artifact,
- const QString &defaultModuleName);
+ static JSValue translateFileConfig(ScriptEngine *engine, Artifact *artifact,
+ const QString &defaultModuleName);
ResolvedProductPtr product() const;
- void setupInputs(const QScriptValue &targetScriptValue);
- void setupOutputs(QScriptValue targetScriptValue);
- void setupExplicitlyDependsOn(QScriptValue targetScriptValue);
+ void setupInputs(ScriptEngine *engine, const JSValue &targetScriptValue);
+ void setupOutputs(ScriptEngine *engine, JSValue targetScriptValue);
+ void setupExplicitlyDependsOn(ScriptEngine *engine, JSValue targetScriptValue);
void createCommands(ScriptEngine *engine, const PrivateScriptFunction &script,
- const QScriptValueList &args);
+ const JSValueList &args);
void rescueChangeTrackingData(const TransformerConstPtr &other);
Set<QString> jobPools() const;
@@ -124,14 +125,13 @@ public:
private:
Transformer();
- AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue,
+ AbstractCommandPtr createCommandFromScriptValue(ScriptEngine *engine, const JSValue &scriptValue,
const CodeLocation &codeLocation);
- static void setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
- const QString &defaultModuleName);
- static QScriptValue translateInOutputs(ScriptEngine *scriptEngine,
- const ArtifactSet &artifacts,
- const QString &defaultModuleName);
+ static void setupInputs(ScriptEngine *engine, JSValue targetScriptValue,
+ const ArtifactSet &inputs, const QString &defaultModuleName);
+ static JSValue translateInOutputs(ScriptEngine *engine, const ArtifactSet &artifacts,
+ const QString &defaultModuleName);
};
} // namespace Internal
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index 169def5cb..1012c1baa 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -7,20 +7,10 @@ QbsLibrary {
name: "Qt.core5compat";
condition: Utilities.versionCompare(Qt.core.version, "6.0.0") >= 0
}
- Depends {
- name: "Qt.script"
- condition: !qbsbuildconfig.useBundledQtScript
- required: false
- }
- Depends {
- name: "qbsscriptengine"
- condition: qbsbuildconfig.useBundledQtScript || !Qt.script.present
- }
+ Depends { name: "quickjs" }
Depends { name: "qbspkgconfig" }
name: "qbscore"
- property stringList bundledQtScriptIncludes: qbsbuildconfig.useBundledQtScript
- || !Qt.script.present ? qbsscriptengine.includePaths : []
- cpp.includePaths: base.concat(bundledQtScriptIncludes).concat([
+ cpp.includePaths: base.concat([
".",
"../.." // for the plugin headers
])
@@ -172,7 +162,6 @@ QbsLibrary {
"rulesapplicator.h",
"rulesevaluationcontext.cpp",
"rulesevaluationcontext.h",
- "scriptclasspropertyiterator.h",
"timestampsupdater.cpp",
"timestampsupdater.h",
"transformer.cpp",
@@ -233,6 +222,7 @@ QbsLibrary {
"file.cpp",
"fileinfoextension.cpp",
"host.cpp",
+ "jsextension.h",
"jsextensions.cpp",
"jsextensions.h",
"moduleproperties.cpp",
@@ -260,9 +250,6 @@ QbsLibrary {
prefix: "jsextensions/"
condition: qbs.targetOS.contains("darwin")
files: [
- // This is ugly, but because of QBS-1592 we cannot check the platform in the header
- // using Q_OS_DARWIN
- "propertylist_darwin.h",
"propertylist_darwin.mm",
"propertylistutils.h",
"propertylistutils.mm",
@@ -283,11 +270,8 @@ QbsLibrary {
"builtindeclarations.cpp",
"builtindeclarations.h",
"deprecationinfo.h",
- "evaluationdata.h",
"evaluator.cpp",
"evaluator.h",
- "evaluatorscriptclass.cpp",
- "evaluatorscriptclass.h",
"filecontext.cpp",
"filecontext.h",
"filecontextbase.cpp",
diff --git a/src/lib/corelib/jsextensions/binaryfile.cpp b/src/lib/corelib/jsextensions/binaryfile.cpp
index 3731c4da7..e4d2583dd 100644
--- a/src/lib/corelib/jsextensions/binaryfile.cpp
+++ b/src/lib/corelib/jsextensions/binaryfile.cpp
@@ -39,6 +39,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/hostosinfo.h>
@@ -46,84 +48,99 @@
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qvariant.h>
-
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
namespace qbs {
namespace Internal {
-class BinaryFile : public QObject, public QScriptable, public ResourceAcquiringScriptObject
+class BinaryFile : public JsExtension<BinaryFile>
{
- Q_OBJECT
+ friend class JsExtension<BinaryFile>;
public:
enum OpenMode {
ReadOnly = 1,
WriteOnly = 2,
ReadWrite = ReadOnly | WriteOnly
};
- Q_ENUM(OpenMode)
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
-
- Q_INVOKABLE void close();
- Q_INVOKABLE QString filePath();
- Q_INVOKABLE bool atEof() const;
- Q_INVOKABLE qint64 size() const;
- Q_INVOKABLE void resize(qint64 size);
- Q_INVOKABLE qint64 pos() const;
- Q_INVOKABLE void seek(qint64 pos);
- Q_INVOKABLE QVariantList read(qint64 size);
- Q_INVOKABLE void write(const QVariantList &data);
+ static const char *name() { return "BinaryFile"; }
+ static void declareEnums(JSContext *ctx, JSValue classObj);
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int);
private:
- explicit BinaryFile(QScriptContext *context, const QString &filePath, OpenMode mode = ReadOnly);
-
- bool checkForClosed() const;
-
- // ResourceAcquiringScriptObject implementation
- void releaseResources() override;
+ static void setupMethods(JSContext *ctx, JSValue obj);
+
+ DEFINE_JS_FORWARDER(jsClose, &BinaryFile::close, "BinaryFile.close")
+ DEFINE_JS_FORWARDER(jsFilePath, &BinaryFile::filePath, "BinaryFile.filePath")
+ DEFINE_JS_FORWARDER(jsAtEof, &BinaryFile::atEof, "BinaryFile.atEof")
+ DEFINE_JS_FORWARDER(jsSize, &BinaryFile::size, "BinaryFile.size")
+ DEFINE_JS_FORWARDER(jsResize, &BinaryFile::resize, "BinaryFile.resize")
+ DEFINE_JS_FORWARDER(jsPos, &BinaryFile::pos, "BinaryFile.pos")
+ DEFINE_JS_FORWARDER(jsSeek, &BinaryFile::seek, "BinaryFile.seek")
+ DEFINE_JS_FORWARDER(jsRead, &BinaryFile::read, "BinaryFile.read")
+ DEFINE_JS_FORWARDER(jsWrite, &BinaryFile::write, "BinaryFile.write")
+
+ void close();
+ QString filePath();
+ bool atEof() const;
+ qint64 size() const;
+ void resize(qint64 size);
+ qint64 pos() const;
+ void seek(qint64 pos);
+ QByteArray read(qint64 size);
+ void write(const QByteArray &data);
+
+ explicit BinaryFile(JSContext *, const QString &filePath, OpenMode mode);
+
+ void checkForClosed() const;
std::unique_ptr<QFile> m_file;
};
-QScriptValue BinaryFile::ctor(QScriptContext *context, QScriptEngine *engine)
+void BinaryFile::declareEnums(JSContext *ctx, JSValue classObj)
{
- BinaryFile *t = nullptr;
- switch (context->argumentCount()) {
- case 0:
- return context->throwError(Tr::tr("BinaryFile constructor needs "
- "path of file to be opened."));
- case 1:
- t = new BinaryFile(context, context->argument(0).toString());
- break;
- case 2:
- t = new BinaryFile(context,
- context->argument(0).toString(),
- static_cast<OpenMode>(context->argument(1).toInt32()));
- break;
- default:
- return context->throwError(Tr::tr("BinaryFile constructor takes at most two parameters."));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addResourceAcquiringScriptObject(t);
- const DubiousContextList dubiousContexts {
- DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- };
- se->checkContext(QStringLiteral("qbs.BinaryFile"), dubiousContexts);
- se->setUsesIo();
+ DECLARE_ENUM(ctx, classObj, ReadOnly);
+ DECLARE_ENUM(ctx, classObj, WriteOnly);
+ DECLARE_ENUM(ctx, classObj, ReadWrite);
+}
- return engine->newQObject(t, QScriptEngine::QtOwnership);
+void BinaryFile::setupMethods(JSContext *ctx, JSValue obj)
+{
+ setupMethod(ctx, obj, "close", &jsClose, 0);
+ setupMethod(ctx, obj, "filePath", &jsFilePath, 0);
+ setupMethod(ctx, obj, "atEof", &jsAtEof, 0);
+ setupMethod(ctx, obj, "size", &jsSize, 0);
+ setupMethod(ctx, obj, "resize", &jsResize, 0);
+ setupMethod(ctx, obj, "pos", &jsPos, 0);
+ setupMethod(ctx, obj, "seek", &jsSeek, 0);
+ setupMethod(ctx, obj, "read", &jsRead, 0);
+ setupMethod(ctx, obj, "write", &jsWrite, 0);
}
-BinaryFile::BinaryFile(QScriptContext *context, const QString &filePath, OpenMode mode)
+JSValue BinaryFile::ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int)
{
- Q_ASSERT(thisObject().engine() == engine());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "BinaryFile constructor", argc, argv);
+ OpenMode mode = ReadOnly;
+ if (argc > 1) {
+ mode = static_cast<OpenMode>
+ (fromArg<qint32>(ctx, "BinaryFile constructor", 2, argv[1]));
+ }
+ const JSValue obj = createObject(ctx, filePath, mode);
+
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts {
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ };
+ se->checkContext(QStringLiteral("qbs.BinaryFile"), dubiousContexts);
+ se->setUsesIo();
+ return obj;
+ } catch (const QString &error) { return throwError(ctx, error); }
+}
+BinaryFile::BinaryFile(JSContext *, const QString &filePath, OpenMode mode)
+{
QIODevice::OpenMode m = QIODevice::NotOpen;
switch (mode) {
case ReadWrite:
@@ -136,130 +153,91 @@ BinaryFile::BinaryFile(QScriptContext *context, const QString &filePath, OpenMod
m = QIODevice::WriteOnly;
break;
default:
- context->throwError(Tr::tr("Unable to open file '%1': Undefined mode '%2'")
- .arg(filePath, mode));
- return;
+ throw Tr::tr("Unable to open file '%1': Undefined mode '%2'").arg(filePath).arg(mode);
}
- m_file = std::make_unique<QFile>(filePath);
- if (Q_UNLIKELY(!m_file->open(m))) {
- context->throwError(Tr::tr("Unable to open file '%1': %2")
- .arg(filePath, m_file->errorString()));
- m_file.reset();
- }
+ auto file = std::make_unique<QFile>(filePath);
+ if (Q_UNLIKELY(!file->open(m)))
+ throw Tr::tr("Unable to open file '%1': %2").arg(filePath, file->errorString());
+ m_file = std::move(file);
}
void BinaryFile::close()
{
- if (checkForClosed())
- return;
+ checkForClosed();
m_file->close();
m_file.reset();
}
QString BinaryFile::filePath()
{
- if (checkForClosed())
- return {};
+ checkForClosed();
return QFileInfo(*m_file).absoluteFilePath();
}
bool BinaryFile::atEof() const
{
- if (checkForClosed())
- return true;
+ checkForClosed();
return m_file->atEnd();
}
qint64 BinaryFile::size() const
{
- if (checkForClosed())
- return -1;
+ checkForClosed();
return m_file->size();
}
void BinaryFile::resize(qint64 size)
{
- if (checkForClosed())
- return;
- if (Q_UNLIKELY(!m_file->resize(size))) {
- context()->throwError(Tr::tr("Could not resize '%1': %2")
- .arg(m_file->fileName(), m_file->errorString()));
- }
+ checkForClosed();
+ if (Q_UNLIKELY(!m_file->resize(size)))
+ throw Tr::tr("Could not resize '%1': %2").arg(m_file->fileName(), m_file->errorString());
}
qint64 BinaryFile::pos() const
{
- if (checkForClosed())
- return -1;
+ checkForClosed();
return m_file->pos();
}
void BinaryFile::seek(qint64 pos)
{
- if (checkForClosed())
- return;
- if (Q_UNLIKELY(!m_file->seek(pos))) {
- context()->throwError(Tr::tr("Could not seek '%1': %2")
- .arg(m_file->fileName(), m_file->errorString()));
- }
+ checkForClosed();
+ if (Q_UNLIKELY(!m_file->seek(pos)))
+ throw Tr::tr("Could not seek '%1': %2").arg(m_file->fileName(), m_file->errorString());
}
-QVariantList BinaryFile::read(qint64 size)
+QByteArray BinaryFile::read(qint64 size)
{
- if (checkForClosed())
- return {};
- const QByteArray bytes = m_file->read(size);
+ checkForClosed();
+ QByteArray bytes = m_file->read(size);
if (Q_UNLIKELY(bytes.size() == 0 && m_file->error() != QFile::NoError)) {
- context()->throwError(Tr::tr("Could not read from '%1': %2")
- .arg(m_file->fileName(), m_file->errorString()));
+ throw (Tr::tr("Could not read from '%1': %2")
+ .arg(m_file->fileName(), m_file->errorString()));
}
-
- return transformed<QVariantList>(bytes, [](const char &c) { return c; });
+ return bytes;
}
-void BinaryFile::write(const QVariantList &data)
+void BinaryFile::write(const QByteArray &data)
{
- if (checkForClosed())
- return;
-
- const auto bytes = transformed<QByteArray>(data, [](const QVariant &v) {
- return char(v.toUInt() & 0xFF); });
-
- const qint64 size = m_file->write(bytes);
+ checkForClosed();
+ const qint64 size = m_file->write(data);
if (Q_UNLIKELY(size == -1)) {
- context()->throwError(Tr::tr("Could not write to '%1': %2")
- .arg(m_file->fileName(), m_file->errorString()));
+ throw Tr::tr("Could not write to '%1': %2")
+ .arg(m_file->fileName(), m_file->errorString());
}
}
-bool BinaryFile::checkForClosed() const
-{
- if (m_file)
- return false;
- if (QScriptContext *ctx = context())
- ctx->throwError(Tr::tr("Access to BinaryFile object that was already closed."));
- return true;
-}
-
-void BinaryFile::releaseResources()
+void BinaryFile::checkForClosed() const
{
- close();
- deleteLater();
+ if (!m_file)
+ throw Tr::tr("Access to BinaryFile object that was already closed.");
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionBinaryFile(QScriptValue extensionObject)
+void initializeJsExtensionBinaryFile(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- const QScriptValue obj = engine->newQMetaObject(&BinaryFile::staticMetaObject,
- engine->newFunction(&BinaryFile::ctor));
- extensionObject.setProperty(QStringLiteral("BinaryFile"), obj);
+ qbs::Internal::BinaryFile::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::BinaryFile *)
-
-#include "binaryfile.moc"
diff --git a/src/lib/corelib/jsextensions/domxml.cpp b/src/lib/corelib/jsextensions/domxml.cpp
index 86e1574c6..84facc970 100644
--- a/src/lib/corelib/jsextensions/domxml.cpp
+++ b/src/lib/corelib/jsextensions/domxml.cpp
@@ -38,428 +38,516 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <QtCore/qfile.h>
-#include <QtCore/qobject.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-#include <QtScript/qscriptable.h>
-
#include <QtXml/qdom.h>
namespace qbs {
namespace Internal {
-class XmlDomDocument;
-
-class XmlDomNode: public QObject, public QScriptable
+template<class C> class XmlDomNode : public JsExtension<XmlDomNode<C>>
{
- Q_OBJECT
public:
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
-
- Q_INVOKABLE bool isElement() const;
- Q_INVOKABLE bool isCDATASection() const;
- Q_INVOKABLE bool isText() const;
-
- Q_INVOKABLE QString attribute(const QString & name, const QString & defValue = QString());
- Q_INVOKABLE void setAttribute(const QString & name, const QString & value);
- Q_INVOKABLE bool hasAttribute(const QString & name) const;
- Q_INVOKABLE QString tagName() const;
- Q_INVOKABLE void setTagName(const QString & name);
-
- Q_INVOKABLE QString text() const;
-
- Q_INVOKABLE QString data() const;
- Q_INVOKABLE void setData(const QString &v) const;
-
- Q_INVOKABLE void clear();
- Q_INVOKABLE bool hasAttributes() const;
- Q_INVOKABLE bool hasChildNodes() const;
- Q_INVOKABLE QScriptValue parentNode() const;
- Q_INVOKABLE QScriptValue firstChild(const QString & tagName = QString());
- Q_INVOKABLE QScriptValue lastChild(const QString & tagName = QString()) const;
- Q_INVOKABLE QScriptValue previousSibling(const QString & tagName = QString()) const;
- Q_INVOKABLE QScriptValue nextSibling(const QString & tagName = QString()) const;
-
- Q_INVOKABLE QScriptValue appendChild(const QScriptValue &newChild);
- Q_INVOKABLE QScriptValue insertBefore(const QScriptValue& newChild, const QScriptValue& refChild);
- Q_INVOKABLE QScriptValue insertAfter(const QScriptValue& newChild, const QScriptValue& refChild);
- Q_INVOKABLE QScriptValue replaceChild(const QScriptValue& newChild, const QScriptValue& oldChild);
- Q_INVOKABLE QScriptValue removeChild(const QScriptValue& oldChild);
-
-protected:
- friend class XmlDomDocument;
- XmlDomNode(const QDomNode &other = QDomNode());
- QDomNode m_domNode;
-};
+ static const char *name();
+ XmlDomNode(const C &value) : m_value(value) {}
+ XmlDomNode(JSContext *, const QString &name);
+ XmlDomNode(JSContext *) {}
-class XmlDomDocument: public XmlDomNode
-{
- Q_OBJECT
-public:
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
- Q_INVOKABLE QScriptValue documentElement();
- Q_INVOKABLE QScriptValue createElement(const QString & tagName);
- Q_INVOKABLE QScriptValue createCDATASection(const QString & value);
- Q_INVOKABLE QScriptValue createTextNode(const QString & value);
-
- Q_INVOKABLE bool setContent(const QString & content);
- Q_INVOKABLE QString toString(int indent = 1);
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int);
+ static void setupMethods(JSContext *ctx, JSValue obj);
- Q_INVOKABLE void save(const QString & filePath, int indent = 1);
- Q_INVOKABLE void load(const QString & filePath);
-
-protected:
- XmlDomDocument(QScriptContext *context, const QString &name = QString());
+ C value() const { return m_value; }
private:
- QDomDocument m_domDocument;
-};
-
-QScriptValue XmlDomDocument::ctor(QScriptContext *context, QScriptEngine *engine)
-{
- XmlDomDocument *xml = nullptr;
- switch (context->argumentCount()) {
- case 0:
- xml = new XmlDomDocument(context);
- break;
- case 1:
- xml = new XmlDomDocument(context, context->argument(0).toString());
- break;
- default:
- return context->throwError(QStringLiteral("DomXml(QString file = QLatin1String(\"\"))"));
- }
- QScriptValue obj = engine->newQObject(xml, QScriptEngine::ScriptOwnership);
- static_cast<ScriptEngine *>(engine)->setUsesIo();
- return obj;
-}
-
-QScriptValue XmlDomDocument::documentElement()
-{
- return engine()->newQObject(new XmlDomNode(m_domDocument.documentElement()), QScriptEngine::ScriptOwnership);
-}
-
-QScriptValue XmlDomDocument::createElement(const QString &tagName)
-{
- return engine()->newQObject(new XmlDomNode(m_domDocument.createElement(tagName)), QScriptEngine::ScriptOwnership);
-}
-
-QScriptValue XmlDomDocument::createCDATASection(const QString &value)
-{
- return engine()->newQObject(new XmlDomNode(m_domDocument.createCDATASection(value)), QScriptEngine::ScriptOwnership);
-}
-
-QScriptValue XmlDomDocument::createTextNode(const QString &value)
-{
- return engine()->newQObject(new XmlDomNode(m_domDocument.createTextNode(value)), QScriptEngine::ScriptOwnership);
-}
-
-bool XmlDomDocument::setContent(const QString &content)
-{
- return m_domDocument.setContent(content);
-}
-
-QString XmlDomDocument::toString(int indent)
-{
- return m_domDocument.toString(indent);
-}
-
-void XmlDomDocument::save(const QString &filePath, int indent)
-{
- QFile f(filePath);
- if (!f.open(QIODevice::WriteOnly)) {
- context()->throwError(QStringLiteral("unable to open '%1'")
- .arg(filePath));
- return;
+#define XML_JS_FWD(name, func, text) DEFINE_JS_FORWARDER_QUAL(XmlDomNode, name, func, text)
+
+ XML_JS_FWD(jsIsElement, &XmlDomNode::isElement, "DomNode.isElement");
+ XML_JS_FWD(jsIsCDATASection, &XmlDomNode::isCDATASection, "DomNode.isCDATASection");
+ XML_JS_FWD(jsIsText, &XmlDomNode::isText, "DomNode.isText");
+ XML_JS_FWD(jsHasAttribute, &XmlDomNode::hasAttribute, "DomNode.hasAttribute");
+ XML_JS_FWD(jsTagName, &XmlDomNode::tagName, "DomNode.tagName");
+ XML_JS_FWD(jsSetTagName, &XmlDomNode::setTagName, "DomNode.setTagName");
+ XML_JS_FWD(jsText, &XmlDomNode::text, "DomNode.text");
+ XML_JS_FWD(jsData, &XmlDomNode::data, "DomNode.data");
+ XML_JS_FWD(jsSetData, &XmlDomNode::setData, "DomNode.setData");
+ XML_JS_FWD(jsClear, &XmlDomNode::clear, "DomNode.clear");
+ XML_JS_FWD(jsHasAttributes, &XmlDomNode::hasAttributes, "DomNode.hasAttributes");
+ XML_JS_FWD(jsHasChildNodes, &XmlDomNode::hasChildNodes, "DomNode.hasChildNodes");
+ XML_JS_FWD(jsSetAttribute, &XmlDomNode::setAttribute, "DomNode.setAttribute");
+ XML_JS_FWD(jsAppendChild, &XmlDomNode::appendChild, "DomNode.appendChild");
+ XML_JS_FWD(jsInsertBefore, &XmlDomNode::insertBefore, "DomNode.insertBefore");
+ XML_JS_FWD(jsInsertAfter, &XmlDomNode::insertAfter, "DomNode.insertAfter");
+ XML_JS_FWD(jsReplaceChild, &XmlDomNode::replaceChild, "DomNode.replaceChild");
+ XML_JS_FWD(jsRemoveChild, &XmlDomNode::removeChild, "DomNode.removeChild");
+
+ static JSValue jsAttribute(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ const auto name = JsExtension<XmlDomNode>::template getArgument<QString>
+ (ctx, "DomNode.attribute", argc, argv);
+ QString defaultValue;
+ if (argc > 1)
+ defaultValue = JsExtension<XmlDomNode>::template fromArg<QString>
+ (ctx, "DomNode.attribute", 2, argv[1]);
+ return makeJsString(ctx, JsExtension<XmlDomNode>::fromJsObject
+ (ctx, this_val)->attribute(name, defaultValue));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-
- QByteArray buff(m_domDocument.toByteArray(indent));
- if (buff.size() != f.write(buff))
+ static JSValue jsParentNode(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- context()->throwError(f.errorString());
- f.close();
- return;
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect(
+ ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->parentNode());
}
-
- f.close();
- if (f.error() != QFile::NoError)
- context()->throwError(f.errorString());
-}
-
-void XmlDomDocument::load(const QString &filePath)
-{
- QFile f(filePath);
- if (!f.open(QIODevice::ReadOnly)) {
- context()->throwError(QStringLiteral("unable to open '%1'")
- .arg(filePath));
- return;
+ static JSValue jsFirstChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ QString tagName;
+ if (argc > 0)
+ tagName = JsExtension<XmlDomNode>::template getArgument<QString>
+ (ctx, "DomNode.firstChild", argc, argv);
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect
+ (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->firstChild(tagName));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+ }
+ static JSValue jsLastChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ QString tagName;
+ if (argc > 0)
+ tagName = JsExtension<XmlDomNode>::template getArgument<QString>
+ (ctx, "DomNode.lastChild", argc, argv);
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect
+ (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->lastChild(tagName));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+ }
+ static JSValue jsPreviousSibling(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ try {
+ QString tagName;
+ if (argc > 0)
+ tagName = JsExtension<XmlDomNode>::template getArgument<QString>
+ (ctx, "DomNode.previousSibling", argc, argv);
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect(ctx, JsExtension<XmlDomNode>::fromJsObject
+ (ctx, this_val)->previousSibling(tagName));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+ }
+ static JSValue jsNextSibling(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ try {
+ QString tagName;
+ if (argc > 0)
+ tagName = JsExtension<C>::template getArgument<QString>
+ (ctx, "Xml.DomElement.nextSibling", argc, argv);
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect
+ (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->nextSibling(tagName));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
- QString errorMsg;
- if (!m_domDocument.setContent(&f, &errorMsg)) {
- context()->throwError(errorMsg);
- return;
+ // Implemented for QDomDocument only.
+ static JSValue jsDocumentElement(JSContext *ctx, JSValueConst this_val, int, JSValueConst *);
+ static JSValue jsCreateElement(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+ static JSValue jsCreateCDATASection(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+ static JSValue jsCreateTextNode(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+ static JSValue jsSetContent(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+ static JSValue jsToString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+ static JSValue jsSave(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+ static JSValue jsLoad(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+
+ bool isElement() const { return m_value.isElement(); }
+ bool isCDATASection() const { return m_value.isCDATASection(); }
+ bool isText() const { return m_value.isText(); }
+ QString attribute(const QString &name, const QString &defaultValue)
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ return el.attribute(name, defaultValue);
+ }
+ void setAttribute(const QString &name, const QString &value)
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ el.setAttribute(name, value);
+ }
+ bool hasAttribute(const QString &name) const
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ return el.hasAttribute(name);
+ }
+ QString tagName() const
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ return el.tagName();
+ }
+ void setTagName(const QString &name)
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ el.setTagName(name);
}
-}
-XmlDomDocument::XmlDomDocument(QScriptContext *context, const QString &name):m_domDocument(name)
-{
- Q_UNUSED(context)
- m_domNode = m_domDocument;
-}
+ QString text() const
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ return el.text();
+ }
-QScriptValue XmlDomNode::ctor(QScriptContext *context, QScriptEngine *engine)
-{
- Q_UNUSED(context)
- return engine->newQObject(new XmlDomNode(), QScriptEngine::ScriptOwnership);
-}
+ QString data() const
+ {
+ if (m_value.isText())
+ return m_value.toText().data();
+ if (m_value.isCDATASection())
+ return m_value.toCDATASection().data();
+ if (m_value.isCharacterData())
+ return m_value.toCharacterData().data();
+ throw QStringLiteral("Node '%1' is not a character data node").arg(m_value.nodeName());
+ }
+ void setData(const QString &v) const
+ {
+ if (m_value.isText())
+ return m_value.toText().setData(v);
+ if (m_value.isCDATASection())
+ return m_value.toCDATASection().setData(v);
+ if (m_value.isCharacterData())
+ return m_value.toCharacterData().setData(v);
+ throw QStringLiteral("Node '%1' is not a character data node").arg(m_value.nodeName());
+ }
+ void clear() { m_value.clear(); }
+ bool hasAttributes() const { return m_value.hasAttributes(); }
+ bool hasChildNodes() const { return m_value.hasChildNodes(); }
-bool XmlDomNode::isElement() const
-{
- return m_domNode.isElement();
-}
+ XmlDomNode<QDomNode> *parentNode() const
+ {
+ return new XmlDomNode<QDomNode>(m_value.parentNode());
+ }
+ XmlDomNode<QDomNode> *firstChild(const QString &tagName)
+ {
+ if (tagName.isEmpty())
+ return new XmlDomNode<QDomNode>(m_value.firstChild());
+ return new XmlDomNode<QDomNode>(m_value.firstChildElement(tagName));
+ }
+ XmlDomNode<QDomNode> *lastChild(const QString &tagName) const
+ {
+ if (tagName.isEmpty())
+ return new XmlDomNode<QDomNode>(m_value.lastChild());
+ return new XmlDomNode<QDomNode>(m_value.lastChildElement(tagName));
+ }
+ XmlDomNode<QDomNode> *previousSibling(const QString &tagName) const
+ {
+ if (tagName.isEmpty())
+ return new XmlDomNode<QDomNode>(m_value.previousSibling());
+ return new XmlDomNode<QDomNode>(m_value.previousSiblingElement(tagName));
+ }
+ XmlDomNode<QDomNode> *nextSibling(const QString &tagName) const
+ {
+ if (tagName.isEmpty())
+ return new XmlDomNode<QDomNode>(m_value.nextSibling());
+ return new XmlDomNode<QDomNode>(m_value.nextSiblingElement(tagName));
+ }
+ void appendChild(const XmlDomNode<QDomNode> *newChild)
+ {
+ m_value.appendChild(newChild->value());
+ }
+ void insertBefore(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *refChild)
+ {
+ m_value.insertBefore(newChild->value(), refChild->value());
+ }
+ void insertAfter(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *refChild)
+ {
+ m_value.insertAfter(newChild->value(), refChild->value());
+ }
+ void replaceChild(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *oldChild)
+ {
+ m_value.replaceChild(newChild->value(), oldChild->value());
+ }
+ void removeChild(const XmlDomNode<QDomNode> *oldChild)
+ {
+ m_value.removeChild(oldChild->value());
+ }
-bool XmlDomNode::isCDATASection() const
-{
- return m_domNode.isCDATASection();
-}
+ // Implemented for QDomDocument only.
+ XmlDomNode<QDomNode> *documentElement();
+ XmlDomNode<QDomNode> *createElement(const QString &tagName);
+ XmlDomNode<QDomNode> *createCDATASection(const QString &value);
+ XmlDomNode<QDomNode> *createTextNode(const QString &value);
-bool XmlDomNode::isText() const
-{
- return m_domNode.isText();
-}
+ bool setContent(const QString &content);
+ QString toString(int indent = 1);
-QString XmlDomNode::attribute(const QString &name, const QString &defValue)
-{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return defValue;
- }
- return el.attribute(name, defValue);
-}
+ void save(const QString &filePath, int indent = 1);
+ void load(const QString &filePath);
-void XmlDomNode::setAttribute(const QString &name, const QString &value)
-{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return;
- }
- el.setAttribute(name, value);
-}
+ C m_value;
+};
-bool XmlDomNode::hasAttribute(const QString &name) const
-{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return false;
- }
- return el.hasAttribute(name);
-}
+template<> const char *XmlDomNode<QDomNode>::name() { return "DomNode"; }
+template<> const char *XmlDomNode<QDomDocument>::name() { return "DomDocument"; }
-QString XmlDomNode::tagName() const
+template<> JSValue XmlDomNode<QDomNode>::ctor(JSContext *ctx, JSValue, JSValue, int , JSValue *,
+ int)
{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return {};
- }
- return el.tagName();
+ const JSValue obj = createObject(ctx);
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ };
+ se->checkContext(QStringLiteral("qbs.Xml.DomNode"), dubiousContexts);
+ se->setUsesIo();
+ return obj;
}
-void XmlDomNode::setTagName(const QString &name)
+template<> JSValue XmlDomNode<QDomDocument>::ctor(JSContext *ctx, JSValue, JSValue,
+ int argc, JSValue *argv, int)
{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return;
- }
- el.setTagName(name);
+ try {
+ JSValue obj;
+ if (argc == 0) {
+ obj = createObject(ctx);
+ } else {
+ const auto name = getArgument<QString>(ctx, "XmlDomDocument constructor",
+ argc, argv);
+ obj = createObject(ctx, name);
+ }
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ };
+ se->checkContext(QStringLiteral("qbs.Xml.DomDocument"), dubiousContexts);
+ se->setUsesIo();
+ return obj;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QString XmlDomNode::text() const
+template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::documentElement()
{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return {};
- }
- return el.text();
+ return new XmlDomNode<QDomNode>(m_value.documentElement());
}
-QString XmlDomNode::data() const
+template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createElement(const QString &tagName)
{
- if (m_domNode.isText())
- return m_domNode.toText().data();
- if (m_domNode.isCDATASection())
- return m_domNode.toCDATASection().data();
- if (m_domNode.isCharacterData())
- return m_domNode.toCharacterData().data();
- context()->throwError(QStringLiteral("Node '%1' is not a character data node").arg(m_domNode.nodeName()));
- return {};
+ return new XmlDomNode<QDomNode>(m_value.createElement(tagName));
}
-void XmlDomNode::setData(const QString &v) const
+template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createCDATASection(const QString &value)
{
- if (m_domNode.isText())
- return m_domNode.toText().setData(v);
- if (m_domNode.isCDATASection())
- return m_domNode.toCDATASection().setData(v);
- if (m_domNode.isCharacterData())
- return m_domNode.toCharacterData().setData(v);
- context()->throwError(QStringLiteral("Node '%1' is not a character data node").arg(m_domNode.nodeName()));
+ return new XmlDomNode<QDomNode>(m_value.createCDATASection(value));
}
-void XmlDomNode::clear()
+template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createTextNode(const QString &value)
{
- m_domNode.clear();
+ return new XmlDomNode<QDomNode>(m_value.createTextNode(value));
}
-bool XmlDomNode::hasAttributes() const
+template<> bool XmlDomNode<QDomDocument>::setContent(const QString &content)
{
- return m_domNode.hasAttributes();
+ return m_value.setContent(content);
}
-bool XmlDomNode::hasChildNodes() const
+template<> QString XmlDomNode<QDomDocument>::toString(int indent)
{
- return m_domNode.hasChildNodes();
+ return m_value.toString(indent);
}
-QScriptValue XmlDomNode::parentNode() const
+template<> JSValue XmlDomNode<QDomDocument>::jsDocumentElement(JSContext *ctx, JSValue this_val,
+ int, JSValue *)
{
- return engine()->newQObject(new XmlDomNode(m_domNode.parentNode()), QScriptEngine::ScriptOwnership);
+ return XmlDomNode<QDomNode>::createObjectDirect(
+ ctx, fromJsObject(ctx, this_val)->documentElement());
}
-QScriptValue XmlDomNode::firstChild(const QString &tagName)
+template<> JSValue XmlDomNode<QDomDocument>::jsCreateElement(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- if (tagName.isEmpty())
- return engine()->newQObject(new XmlDomNode(m_domNode.firstChild()), QScriptEngine::ScriptOwnership);
- return engine()->newQObject(new XmlDomNode(m_domNode.firstChildElement(tagName)), QScriptEngine::ScriptOwnership);
+ try {
+ const auto tagName = getArgument<QString>(ctx, "DomDocument.createElement", argc, argv);
+ const JSValue obj = XmlDomNode<QDomNode>::createObjectDirect(
+ ctx, fromJsObject(ctx, this_val)->createElement(tagName));
+ return obj;
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue XmlDomNode::lastChild(const QString &tagName) const
+template<> JSValue XmlDomNode<QDomDocument>::jsCreateCDATASection(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- if (tagName.isEmpty())
- return engine()->newQObject(new XmlDomNode(m_domNode.lastChild()), QScriptEngine::ScriptOwnership);
- return engine()->newQObject(new XmlDomNode(m_domNode.lastChildElement(tagName)), QScriptEngine::ScriptOwnership);
+ try {
+ const auto value = getArgument<QString>(ctx, "DomDocument.createCDATASection", argc, argv);
+ return XmlDomNode<QDomNode>::createObjectDirect(
+ ctx, fromJsObject(ctx, this_val)->createCDATASection(value));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue XmlDomNode::previousSibling(const QString &tagName) const
+template<> JSValue XmlDomNode<QDomDocument>::jsCreateTextNode(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- if (tagName.isEmpty())
- return engine()->newQObject(new XmlDomNode(m_domNode.previousSibling()), QScriptEngine::ScriptOwnership);
- return engine()->newQObject(new XmlDomNode(m_domNode.previousSiblingElement(tagName)), QScriptEngine::ScriptOwnership);
+ try {
+ const auto value = getArgument<QString>(ctx, "DomDocument.createTextNode", argc, argv);
+ return XmlDomNode<QDomNode>::createObjectDirect(
+ ctx, fromJsObject(ctx, this_val)->createTextNode(value));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue XmlDomNode::nextSibling(const QString &tagName) const
+template<> JSValue XmlDomNode<QDomDocument>::jsSetContent(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- if (tagName.isEmpty())
- return engine()->newQObject(new XmlDomNode(m_domNode.nextSibling()), QScriptEngine::ScriptOwnership);
- return engine()->newQObject(new XmlDomNode(m_domNode.nextSiblingElement(tagName)), QScriptEngine::ScriptOwnership);
+ try {
+ const auto content = getArgument<QString>(ctx, "DomDocument.setContent", argc, argv);
+ return JS_NewBool(ctx, fromJsObject(ctx, this_val)->setContent(content));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue XmlDomNode::appendChild(const QScriptValue &newChild)
+template<> JSValue XmlDomNode<QDomDocument>::jsToString(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
- if (!newNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
+ try {
+ qint32 indent = 1;
+ if (argc > 0)
+ indent = getArgument<qint32>(ctx, "DomDocument.toString", argc, argv);
+ return makeJsString(ctx, fromJsObject(ctx, this_val)->toString(indent));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
- return engine()->newQObject(new XmlDomNode(m_domNode.appendChild(newNode->m_domNode)), QScriptEngine::ScriptOwnership);
}
-QScriptValue XmlDomNode::insertBefore(const QScriptValue &newChild, const QScriptValue &refChild)
+template<> void XmlDomNode<QDomDocument>::save(const QString &filePath, int indent)
{
- auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
- if (!newNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
- }
+ QFile f(filePath);
+ if (!f.open(QIODevice::WriteOnly))
+ throw QStringLiteral("unable to open '%1'").arg(filePath);
- auto refNode = qobject_cast<XmlDomNode*>(refChild.toQObject());
- if (!refNode) {
- context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object"));
- return {};
- }
+ QByteArray buff(m_value.toByteArray(indent));
+ if (buff.size() != f.write(buff))
+ throw f.errorString();
- return engine()->newQObject(new XmlDomNode(m_domNode.insertBefore(newNode->m_domNode, refNode->m_domNode)), QScriptEngine::ScriptOwnership);
+ f.close();
+ if (f.error() != QFile::NoError)
+ throw f.errorString();
}
-QScriptValue XmlDomNode::insertAfter(const QScriptValue &newChild, const QScriptValue &refChild)
+template<> JSValue XmlDomNode<QDomDocument>::jsSave(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
- if (!newNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
- }
-
- auto refNode = qobject_cast<XmlDomNode*>(refChild.toQObject());
- if (!refNode) {
- context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object"));
- return {};
+ try {
+ const auto filePath = getArgument<QString>(ctx, "DomDocument.save", argc, argv);
+ qint32 indent = 1;
+ if (argc > 1)
+ indent = fromArg<qint32>(ctx, "toString", 2, argv[1]);
+ fromJsObject(ctx, this_val)->save(filePath, indent);
+ return JS_UNDEFINED;
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- return engine()->newQObject(new XmlDomNode(m_domNode.insertAfter(newNode->m_domNode, refNode->m_domNode)), QScriptEngine::ScriptOwnership);
}
-QScriptValue XmlDomNode::replaceChild(const QScriptValue &newChild, const QScriptValue &oldChild)
+template<> void XmlDomNode<QDomDocument>::load(const QString &filePath)
{
- auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
- if (!newNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
- }
-
- auto oldNode = qobject_cast<XmlDomNode*>(oldChild.toQObject());
- if (!oldNode) {
- context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object"));
- return {};
- }
+ QFile f(filePath);
+ if (!f.open(QIODevice::ReadOnly))
+ throw QStringLiteral("unable to open '%1'").arg(filePath);
- return engine()->newQObject(new XmlDomNode(m_domNode.replaceChild(newNode->m_domNode, oldNode->m_domNode)), QScriptEngine::ScriptOwnership);
+ QString errorMsg;
+ if (!m_value.setContent(&f, &errorMsg))
+ throw errorMsg;
}
-QScriptValue XmlDomNode::removeChild(const QScriptValue &oldChild)
+template<> JSValue XmlDomNode<QDomDocument>::jsLoad(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- auto oldNode = qobject_cast<XmlDomNode*>(oldChild.toQObject());
- if (!oldNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
+ try {
+ const auto filePath = getArgument<QString>(ctx, "DomDocument.load", argc, argv);
+ fromJsObject(ctx, this_val)->load(filePath);
+ return JS_UNDEFINED;
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- return engine()->newQObject(new XmlDomNode(m_domNode.removeChild(oldNode->m_domNode)), QScriptEngine::ScriptOwnership);
}
-XmlDomNode::XmlDomNode(const QDomNode &other)
-{
- m_domNode = other;
+template<> XmlDomNode<QDomDocument>::XmlDomNode(JSContext *, const QString &name) : m_value(name) {}
+
+template<class C>
+void XmlDomNode<C>::setupMethods(JSContext *ctx, JSValue obj)
+{
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "appendChild", &jsAppendChild, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "attribute", &jsAttribute, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "clear", &jsClear, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "data", &jsData, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "firstChild", &jsFirstChild, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasAttribute", &jsHasAttribute, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasAttributes", &jsHasAttributes, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasChildNodes", &jsHasChildNodes, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "insertAfter", &jsInsertAfter, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "insertBefore", &jsInsertBefore, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isCDATASection", &jsIsCDATASection, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isElement", &jsIsElement, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isText", &jsIsText, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "lastChild", &jsLastChild, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "nextSibling", &jsNextSibling, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "parentNode", &jsParentNode, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "previousSibling", &jsPreviousSibling, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "removeChild", &jsRemoveChild, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "replaceChild", &jsReplaceChild, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setAttribute", &jsSetAttribute, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setData", &jsSetData, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setTagName", &jsSetTagName, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "tagName", &jsTagName, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "text", &jsText, 0);
+ if constexpr (std::is_same_v<C, QDomDocument>) {
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "documentElement", &jsDocumentElement, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createElement", &jsCreateElement, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createCDATASection",
+ &jsCreateCDATASection, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createTextNode", &jsCreateTextNode, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setContent", &jsSetContent, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "toString", &jsToString, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "save", &jsSave, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "load", &jsLoad, 1);
+ }
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionXml(QScriptValue extensionObject)
+void initializeJsExtensionXml(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue docObj = engine->newQMetaObject(&XmlDomDocument::staticMetaObject,
- engine->newFunction(&XmlDomDocument::ctor));
- QScriptValue nodeObj = engine->newQMetaObject(&XmlDomNode::staticMetaObject,
- engine->newFunction(&XmlDomNode::ctor));
- QScriptValue contextObject = engine->newObject();
- contextObject.setProperty(QStringLiteral("DomDocument"), docObj);
- contextObject.setProperty(QStringLiteral("DomElement"), nodeObj);
-
- extensionObject.setProperty(QStringLiteral("Xml"), contextObject);
+ JSValue contextObject = engine->newObject();
+ XmlDomNode<QDomNode>::registerClass(engine, contextObject);
+ XmlDomNode<QDomDocument>::registerClass(engine, contextObject);
+ setJsProperty(engine->context(), extensionObject, QLatin1String("Xml"), contextObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::XmlDomDocument *)
-Q_DECLARE_METATYPE(qbs::Internal::XmlDomNode *)
-
-#include "domxml.moc"
diff --git a/src/lib/corelib/jsextensions/environmentextension.cpp b/src/lib/corelib/jsextensions/environmentextension.cpp
index cf17c938b..e973072d0 100644
--- a/src/lib/corelib/jsextensions/environmentextension.cpp
+++ b/src/lib/corelib/jsextensions/environmentextension.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/hostosinfo.h>
@@ -44,93 +46,89 @@
#include <QtCore/qdir.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
-class EnvironmentExtension : public QObject, QScriptable
+class EnvironmentExtension : public JsExtension<EnvironmentExtension>
{
- Q_OBJECT
public:
- void initializeJsExtensionEnvironment(QScriptValue extensionObject);
- static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_getEnv(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_putEnv(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_unsetEnv(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_currentEnv(QScriptContext *context, QScriptEngine *engine);
+ static const char *name() { return "Environment"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static JSValue jsGetEnv(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsPutEnv(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsUnsetEnv(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsCurrentEnv(JSContext *ctx, JSValueConst, int, JSValueConst *);
};
-QScriptValue EnvironmentExtension::js_ctor(QScriptContext *context, QScriptEngine *engine)
+void EnvironmentExtension::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'Environment' cannot be instantiated."));
+ setupMethod(ctx, classObj, "getEnv", &EnvironmentExtension::jsGetEnv, 1);
+ setupMethod(ctx, classObj, "putEnv", &EnvironmentExtension::jsPutEnv, 2);
+ setupMethod(ctx, classObj, "unsetEnv", &EnvironmentExtension::jsUnsetEnv, 1);
+ setupMethod(ctx, classObj, "currentEnv", &EnvironmentExtension::jsCurrentEnv, 0);
}
-static QProcessEnvironment *getProcessEnvironment(QScriptContext *context, QScriptEngine *engine,
- const QString &func, bool doThrow = true)
+static QProcessEnvironment *getProcessEnvironment(ScriptEngine *engine, const QString &func,
+ bool doThrow = true)
{
QVariant v = engine->property(StringConstants::qbsProcEnvVarInternal());
auto procenv = reinterpret_cast<QProcessEnvironment *>(v.value<void *>());
- if (!procenv && doThrow)
- throw context->throwError(QScriptContext::UnknownError,
- QStringLiteral("%1 can only be called from ").arg(func) +
- QStringLiteral("Module.setupBuildEnvironment and ") +
- QStringLiteral("Module.setupRunEnvironment"));
+ if (!procenv && doThrow) {
+ throw QStringLiteral("%1 can only be called from "
+ "Module.setupBuildEnvironment and "
+ "Module.setupRunEnvironment").arg(func);
+ }
return procenv;
}
-QScriptValue EnvironmentExtension::js_getEnv(QScriptContext *context, QScriptEngine *engine)
+JSValue EnvironmentExtension::jsGetEnv(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getEnv expects 1 argument"));
- const QProcessEnvironment env = static_cast<ScriptEngine *>(engine)->environment();
- const QProcessEnvironment *procenv = getProcessEnvironment(context, engine,
- QStringLiteral("getEnv"), false);
- if (!procenv)
- procenv = &env;
-
- const QString name = context->argument(0).toString();
- const QString value = procenv->value(name);
- return value.isNull() ? engine->undefinedValue() : value;
+ try {
+ const auto name = getArgument<QString>(ctx, "Environment.getEnv", argc, argv);
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const QProcessEnvironment env = engine->environment();
+ const QProcessEnvironment *procenv = getProcessEnvironment(engine, QStringLiteral("getEnv"),
+ false);
+ if (!procenv)
+ procenv = &env;
+ const QString value = procenv->value(name);
+ return value.isNull() ? engine->undefinedValue() : makeJsString(ctx, value);
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue EnvironmentExtension::js_putEnv(QScriptContext *context, QScriptEngine *engine)
+JSValue EnvironmentExtension::jsPutEnv(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- if (Q_UNLIKELY(context->argumentCount() != 2))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("putEnv expects 2 arguments"));
- getProcessEnvironment(context, engine, QStringLiteral("putEnv"))->insert(
- context->argument(0).toString(),
- context->argument(1).toString());
- return engine->undefinedValue();
+ try {
+ const auto args = getArguments<QString, QString>(ctx, "Environment.putEnv", argc, argv);
+ getProcessEnvironment(ScriptEngine::engineForContext(ctx), QStringLiteral("putEnv"))
+ ->insert(std::get<0>(args), std::get<1>(args));
+ return JS_UNDEFINED;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue EnvironmentExtension::js_unsetEnv(QScriptContext *context, QScriptEngine *engine)
+JSValue EnvironmentExtension::jsUnsetEnv(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("unsetEnv expects 1 argument"));
- getProcessEnvironment(context, engine, QStringLiteral("unsetEnv"))->remove(
- context->argument(0).toString());
- return engine->undefinedValue();
+ try {
+ const auto name = getArgument<QString>(ctx, "Environment.unsetEnv", argc, argv);
+ getProcessEnvironment(ScriptEngine::engineForContext(ctx), QStringLiteral("unsetEnv"))
+ ->remove(name);
+ return JS_UNDEFINED;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue EnvironmentExtension::js_currentEnv(QScriptContext *context, QScriptEngine *engine)
+JSValue EnvironmentExtension::jsCurrentEnv(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- const QProcessEnvironment env = static_cast<ScriptEngine *>(engine)->environment();
- const QProcessEnvironment *procenv = getProcessEnvironment(context, engine,
- QStringLiteral("currentEnv"), false);
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const QProcessEnvironment env = engine->environment();
+ const QProcessEnvironment *procenv = getProcessEnvironment(engine, QStringLiteral("currentEnv"),
+ false);
if (!procenv)
procenv = &env;
- QScriptValue envObject = engine->newObject();
+ JSValue envObject = engine->newObject();
const auto keys = procenv->keys();
for (const QString &key : keys) {
const QString keyName = HostOsInfo::isWindowsHost() ? key.toUpper() : key;
- envObject.setProperty(keyName, QScriptValue(procenv->value(key)));
+ setJsProperty(ctx, envObject, keyName, procenv->value(key));
}
return envObject;
}
@@ -138,23 +136,7 @@ QScriptValue EnvironmentExtension::js_currentEnv(QScriptContext *context, QScrip
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionEnvironment(QScriptValue extensionObject)
+void initializeJsExtensionEnvironment(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue environmentObj = engine->newQMetaObject(&EnvironmentExtension::staticMetaObject,
- engine->newFunction(&EnvironmentExtension::js_ctor));
- environmentObj.setProperty(QStringLiteral("getEnv"),
- engine->newFunction(EnvironmentExtension::js_getEnv, 1));
- environmentObj.setProperty(QStringLiteral("putEnv"),
- engine->newFunction(EnvironmentExtension::js_putEnv, 2));
- environmentObj.setProperty(QStringLiteral("unsetEnv"),
- engine->newFunction(EnvironmentExtension::js_unsetEnv, 1));
- environmentObj.setProperty(QStringLiteral("currentEnv"),
- engine->newFunction(EnvironmentExtension::js_currentEnv, 0));
- extensionObject.setProperty(QStringLiteral("Environment"), environmentObj);
+ qbs::Internal::EnvironmentExtension::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::EnvironmentExtension *)
-
-#include "environmentextension.moc"
diff --git a/src/lib/corelib/jsextensions/file.cpp b/src/lib/corelib/jsextensions/file.cpp
index cddf2ffa0..6c24635a0 100644
--- a/src/lib/corelib/jsextensions/file.cpp
+++ b/src/lib/corelib/jsextensions/file.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/fileinfo.h>
@@ -44,15 +46,11 @@
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
-class File : public QObject, QScriptable
+class File : public JsExtension<File>
{
- Q_OBJECT
public:
enum Filter {
Dirs = 0x001,
@@ -82,209 +80,198 @@ public:
NoFilter = -1
};
Q_DECLARE_FLAGS(Filters, Filter)
- Q_ENUM(Filter)
- static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_copy(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_exists(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_directoryEntries(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_lastModified(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_makePath(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_move(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_remove(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalFilePath(QScriptContext *context, QScriptEngine *engine);
+ static const char *name() { return "File"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static void declareEnums(JSContext *ctx, JSValue classObj);
+ static JSValue jsCopy(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsExists(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsDirectoryEntries(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsLastModified(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsMakePath(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsMove(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsRemove(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsCanonicalFilePath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
};
-QScriptValue File::js_ctor(QScriptContext *context, QScriptEngine *engine)
+void File::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'File' cannot be instantiated."));
+ setupMethod(ctx, classObj, "copy", &File::jsCopy, 2);
+ setupMethod(ctx, classObj, "exists", &File::jsExists, 1);
+ setupMethod(ctx, classObj, "directoryEntries", File::jsDirectoryEntries, 2);
+ setupMethod(ctx, classObj, "lastModified", File::jsLastModified, 1);
+ setupMethod(ctx, classObj, "makePath", &File::jsMakePath, 1);
+ setupMethod(ctx, classObj, "move", &File::jsMove, 3);
+ setupMethod(ctx, classObj, "remove", &File::jsRemove, 1);
+ setupMethod(ctx, classObj, "canonicalFilePath", &File::jsCanonicalFilePath, 1);
}
-QScriptValue File::js_copy(QScriptContext *context, QScriptEngine *engine)
+void File::declareEnums(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("copy expects 2 arguments"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({
- DubiousContext(EvalContext::PropertyEvaluation),
- DubiousContext(EvalContext::RuleExecution, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("File.copy()"), dubiousContexts);
-
- const QString sourceFile = context->argument(0).toString();
- const QString targetFile = context->argument(1).toString();
- QString errorMessage;
- if (Q_UNLIKELY(!copyFileRecursion(sourceFile, targetFile, true, true, &errorMessage)))
- return context->throwError(errorMessage);
- return true;
+ DECLARE_ENUM(ctx, classObj, Dirs);
+ DECLARE_ENUM(ctx, classObj, Files);
+ DECLARE_ENUM(ctx, classObj, Drives);
+ DECLARE_ENUM(ctx, classObj, NoSymLinks);
+ DECLARE_ENUM(ctx, classObj, AllEntries);
+ DECLARE_ENUM(ctx, classObj, TypeMask);
+ DECLARE_ENUM(ctx, classObj, Readable);
+ DECLARE_ENUM(ctx, classObj, Writable);
+ DECLARE_ENUM(ctx, classObj, Executable);
+ DECLARE_ENUM(ctx, classObj, PermissionMask);
+ DECLARE_ENUM(ctx, classObj, Modified);
+ DECLARE_ENUM(ctx, classObj, Hidden);
+ DECLARE_ENUM(ctx, classObj, System);
+ DECLARE_ENUM(ctx, classObj, AccessMask);
+ DECLARE_ENUM(ctx, classObj, AllDirs);
+ DECLARE_ENUM(ctx, classObj, CaseSensitive);
+ DECLARE_ENUM(ctx, classObj, NoDot);
+ DECLARE_ENUM(ctx, classObj, NoDotDot);
+ DECLARE_ENUM(ctx, classObj, NoDotAndDotDot);
+ DECLARE_ENUM(ctx, classObj, NoFilter);
}
-QScriptValue File::js_exists(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsCopy(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("exist expects 1 argument"));
- }
- const QString filePath = context->argument(0).toString();
- const bool exists = FileInfo::exists(filePath);
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addFileExistsResult(filePath, exists);
- return exists;
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts({
+ DubiousContext(EvalContext::PropertyEvaluation),
+ DubiousContext(EvalContext::RuleExecution, DubiousContext::SuggestMoving)
+ });
+ se->checkContext(QStringLiteral("File.copy()"), dubiousContexts);
+
+ const auto args = getArguments<QString, QString>(ctx, "File.copy", argc, argv);
+ QString errorMessage;
+ if (Q_UNLIKELY(!copyFileRecursion(std::get<0>(args), std::get<1>(args), true, true,
+ &errorMessage))) {
+ throw errorMessage;
+ }
+ return JS_TRUE;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_directoryEntries(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsExists(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("directoryEntries expects 2 arguments"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({
- DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("File.directoryEntries()"), dubiousContexts);
-
- const QString path = context->argument(0).toString();
- const auto filters = static_cast<QDir::Filters>(context->argument(1).toUInt32());
- QDir dir(path);
- const QStringList entries = dir.entryList(filters, QDir::Name);
- se->addDirectoryEntriesResult(path, filters, entries);
- return qScriptValueFromSequence(engine, entries);
+ try {
+ const auto filePath = getArgument<QString>(ctx, "File.exists", argc, argv);
+ const bool exists = FileInfo::exists(filePath);
+ ScriptEngine::engineForContext(ctx)->addFileExistsResult(filePath, exists);
+ return JS_NewBool(ctx, exists);
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_remove(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsDirectoryEntries(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("remove expects 1 argument"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) });
- se->checkContext(QStringLiteral("File.remove()"), dubiousContexts);
-
- QString fileName = context->argument(0).toString();
-
- QString errorMessage;
- if (Q_UNLIKELY(!removeFileRecursion(QFileInfo(fileName), &errorMessage)))
- return context->throwError(errorMessage);
- return true;
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts({
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ });
+ se->checkContext(QStringLiteral("File.directoryEntries()"), dubiousContexts);
+
+ const auto args = getArguments<QString, qint32>(ctx, "Environment.directoryEntries",
+ argc, argv);
+ const QString path = std::get<0>(args);
+ const QDir dir(path);
+ const auto filters = static_cast<QDir::Filters>(std::get<1>(args));
+ const QStringList entries = dir.entryList(filters, QDir::Name);
+ se->addDirectoryEntriesResult(path, filters, entries);
+ return makeJsStringList(ctx, entries);
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_lastModified(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsRemove(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("File.lastModified() expects an argument"));
- }
- const QString filePath = context->argument(0).toString();
- const FileTime timestamp = FileInfo(filePath).lastModified();
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addFileLastModifiedResult(filePath, timestamp);
- return timestamp.asDouble();
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{DubiousContext(EvalContext::PropertyEvaluation)};
+ se->checkContext(QStringLiteral("File.remove()"), dubiousContexts);
+ const auto fileName = getArgument<QString>(ctx, "Environment.remove", argc, argv);
+ QString errorMessage;
+ if (Q_UNLIKELY(!removeFileRecursion(QFileInfo(fileName), &errorMessage)))
+ throw errorMessage;
+ return JS_TRUE;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_makePath(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsLastModified(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("makePath expects 1 argument"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) });
- se->checkContext(QStringLiteral("File.makePath()"), dubiousContexts);
-
- return QDir::root().mkpath(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "Environment.lastModified", argc, argv);
+ const FileTime timestamp = FileInfo(filePath).lastModified();
+ const auto se = ScriptEngine::engineForContext(ctx);
+ se->addFileLastModifiedResult(filePath, timestamp);
+ return JS_NewFloat64(ctx, timestamp.asDouble());
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_move(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsMakePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("move expects 2 arguments"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) });
- se->checkContext(QStringLiteral("File.move()"), dubiousContexts);
-
- const QString sourceFile = context->argument(0).toString();
- const QString targetFile = context->argument(1).toString();
- const bool overwrite = context->argumentCount() > 2 ? context->argument(2).toBool() : true;
-
- if (Q_UNLIKELY(QFileInfo(sourceFile).isDir()))
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': "
- "Source file path is a directory.")
- .arg(sourceFile, targetFile));
-
- if (Q_UNLIKELY(QFileInfo(targetFile).isDir())) {
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': "
- "Destination file path is a directory.")
- .arg(sourceFile, targetFile));
- }
-
- QFile f(targetFile);
- if (overwrite && f.exists() && !f.remove())
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': %3")
- .arg(sourceFile, targetFile, f.errorString()));
-
- if (QFile::exists(targetFile))
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': "
- "Destination file exists.")
- .arg(sourceFile, targetFile));
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{DubiousContext(EvalContext::PropertyEvaluation)};
+ se->checkContext(QStringLiteral("File.makePath()"), dubiousContexts);
+ const auto path = getArgument<QString>(ctx, "File.makePath", argc, argv);
+ return JS_NewBool(ctx, QDir::root().mkpath(path));
+ } catch (const QString &error) { return throwError(ctx, error); }
+}
- QFile f2(sourceFile);
- if (Q_UNLIKELY(!f2.rename(targetFile)))
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': %3")
- .arg(sourceFile, targetFile, f2.errorString()));
- return true;
+JSValue File::jsMove(JSContext *ctx, JSValue, int argc, JSValue *argv)
+{
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{DubiousContext(EvalContext::PropertyEvaluation)};
+ se->checkContext(QStringLiteral("File.move()"), dubiousContexts);
+
+ const auto args = getArguments<QString, QString>(ctx, "File.move", argc, argv);
+ const QString sourceFile = std::get<0>(args);
+ const QString targetFile = std::get<1>(args);
+ const auto overwrite = argc > 2 ? fromArg<bool>(ctx, "File.move", 3, argv[2]) : true;
+
+ if (Q_UNLIKELY(QFileInfo(sourceFile).isDir())) {
+ throw QStringLiteral("Could not move '%1' to '%2': "
+ "Source file path is a directory.").arg(sourceFile, targetFile);
+ }
+ if (Q_UNLIKELY(QFileInfo(targetFile).isDir())) {
+ throw QStringLiteral("Could not move '%1' to '%2': "
+ "Destination file path is a directory.")
+ .arg(sourceFile, targetFile);
+ }
+
+ QFile f(targetFile);
+ if (overwrite && f.exists() && !f.remove()) {
+ throw QStringLiteral("Could not move '%1' to '%2': %3")
+ .arg(sourceFile, targetFile, f.errorString());
+ }
+ if (QFile::exists(targetFile)) {
+ throw QStringLiteral("Could not move '%1' to '%2': "
+ "Destination file exists.").arg(sourceFile, targetFile);
+ }
+
+ QFile f2(sourceFile);
+ if (Q_UNLIKELY(!f2.rename(targetFile))) {
+ throw QStringLiteral("Could not move '%1' to '%2': %3")
+ .arg(sourceFile, targetFile, f2.errorString());
+ }
+ return JS_TRUE;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_canonicalFilePath(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsCanonicalFilePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("canonicalFilePath expects 1 argument"));
- }
- return QFileInfo(context->argument(0).toString()).canonicalFilePath();
+ try {
+ const auto path = getArgument<QString>(ctx, "File.canonicalFilePath", argc, argv);
+ return makeJsString(ctx, QFileInfo(path).canonicalFilePath());
+ } catch (const QString &error) { return throwError(ctx, error); }
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionFile(QScriptValue extensionObject)
+void initializeJsExtensionFile(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue fileObj = engine->newQMetaObject(&File::staticMetaObject,
- engine->newFunction(&File::js_ctor));
- fileObj.setProperty(QStringLiteral("copy"), engine->newFunction(File::js_copy));
- fileObj.setProperty(QStringLiteral("exists"), engine->newFunction(File::js_exists));
- fileObj.setProperty(QStringLiteral("directoryEntries"),
- engine->newFunction(File::js_directoryEntries));
- fileObj.setProperty(QStringLiteral("lastModified"), engine->newFunction(File::js_lastModified));
- fileObj.setProperty(QStringLiteral("makePath"), engine->newFunction(File::js_makePath));
- fileObj.setProperty(QStringLiteral("move"), engine->newFunction(File::js_move));
- fileObj.setProperty(QStringLiteral("remove"), engine->newFunction(File::js_remove));
- fileObj.setProperty(QStringLiteral("canonicalFilePath"),
- engine->newFunction(File::js_canonicalFilePath));
- extensionObject.setProperty(QStringLiteral("File"), fileObj);
+ qbs::Internal::File::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::File *)
-
-#include "file.moc"
diff --git a/src/lib/corelib/jsextensions/fileinfoextension.cpp b/src/lib/corelib/jsextensions/fileinfoextension.cpp
index a4986d12c..cfc45ca40 100644
--- a/src/lib/corelib/jsextensions/fileinfoextension.cpp
+++ b/src/lib/corelib/jsextensions/fileinfoextension.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/fileinfo.h>
@@ -45,9 +47,6 @@
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
#include <regex>
namespace qbs {
@@ -63,300 +62,253 @@ static QString uniqueSeparators(QString path)
return path;
}
-class FileInfoExtension : public QObject, QScriptable
+class FileInfoExtension : public JsExtension<FileInfoExtension>
{
- Q_OBJECT
public:
- static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_path(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_fileName(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_baseName(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_suffix(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_completeSuffix(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalPath(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_cleanPath(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_completeBaseName(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_relativePath(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_resolvePath(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_isAbsolutePath(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_toWindowsSeparators(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_fromWindowsSeparators(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_toNativeSeparators(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_fromNativeSeparators(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_joinPaths(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_pathListSeparator(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_pathSeparator(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_executableSuffix(QScriptContext *context, QScriptEngine *engine);
+ static const char *name() { return "FileInfo"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static JSValue jsPath(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsFileName(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsBaseName(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsSuffix(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsCompleteSuffix(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsCanonicalPath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsCleanPath(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsCompleteBaseName(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsRelativePath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsResolvePath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsIsAbsolutePath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsToWindowsSeparators(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsFromWindowsSeparators(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsToNativeSeparators(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsFromNativeSeparators(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsJoinPaths(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsPathListSeparator(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsPathSeparator(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsExecutableSuffix(JSContext *ctx, JSValueConst, int, JSValueConst *);
};
-QScriptValue FileInfoExtension::js_ctor(QScriptContext *context, QScriptEngine *engine)
+void FileInfoExtension::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'FileInfo' cannot be instantiated."));
+ setupMethod(ctx, classObj, StringConstants::fileInfoPath(), &FileInfoExtension::jsPath, 1);
+ setupMethod(ctx, classObj, StringConstants::fileInfoFileName(),
+ &FileInfoExtension::jsFileName, 1);
+ setupMethod(ctx, classObj, StringConstants::baseNameProperty(),
+ &FileInfoExtension::jsBaseName, 1);
+ setupMethod(ctx, classObj, "suffix", &FileInfoExtension::jsSuffix, 1);
+ setupMethod(ctx, classObj, "completeSuffix", &FileInfoExtension::jsCompleteSuffix, 1);
+ setupMethod(ctx, classObj, "canonicalPath", &FileInfoExtension::jsCanonicalPath, 1);
+ setupMethod(ctx, classObj, "cleanPath", &FileInfoExtension::jsCleanPath, 1);
+ setupMethod(ctx, classObj, StringConstants::completeBaseNameProperty(),
+ &FileInfoExtension::jsCompleteBaseName, 1);
+ setupMethod(ctx, classObj, "relativePath", &FileInfoExtension::jsRelativePath, 1);
+ setupMethod(ctx, classObj, "resolvePath", &FileInfoExtension::jsResolvePath, 1);
+ setupMethod(ctx, classObj, "isAbsolutePath", &FileInfoExtension::jsIsAbsolutePath, 1);
+ setupMethod(ctx, classObj, "toWindowsSeparators",
+ &FileInfoExtension::jsToWindowsSeparators, 1);
+ setupMethod(ctx, classObj, "fromWindowsSeparators",
+ &FileInfoExtension::jsFromWindowsSeparators, 1);
+ setupMethod(ctx, classObj, "toNativeSeparators",
+ &FileInfoExtension::jsToNativeSeparators, 1);
+ setupMethod(ctx, classObj, "fromNativeSeparators",
+ &FileInfoExtension::jsFromNativeSeparators, 1);
+ setupMethod(ctx, classObj, "joinPaths", &FileInfoExtension::jsJoinPaths, 0);
+ setupMethod(ctx, classObj, "pathListSeparator",
+ &FileInfoExtension::jsPathListSeparator, 0);
+ setupMethod(ctx, classObj, "pathSeparator", &FileInfoExtension::jsPathSeparator, 0);
+ setupMethod(ctx, classObj, "executableSuffix", &FileInfoExtension::jsExecutableSuffix, 0);
}
-QScriptValue FileInfoExtension::js_path(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsPath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("path expects 1 argument"));
- }
- HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
- if (context->argumentCount() > 1) {
- hostOs = context->argument(1).toVariant().toStringList().contains(QLatin1String("windows"))
- ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
- }
- return FileInfo::path(context->argument(0).toString(), hostOs);
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.path", argc, argv);
+ HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
+ if (argc > 1) {
+ const auto osList = fromArg<QStringList>(ctx, "FileInfo.path", 2, argv[1]);
+ hostOs = osList.contains(QLatin1String("windows"))
+ ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
+ }
+ return makeJsString(ctx, FileInfo::path(filePath, hostOs));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_fileName(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsFileName(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("fileName expects 1 argument"));
- }
- return FileInfo::fileName(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.fileName", argc, argv);
+ return makeJsString(ctx, FileInfo::fileName(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_baseName(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsBaseName(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("baseName expects 1 argument"));
- }
- return FileInfo::baseName(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.baseName", argc, argv);
+ return makeJsString(ctx, FileInfo::baseName(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_suffix(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsSuffix(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("suffix expects 1 argument"));
- }
- return FileInfo::suffix(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.suffix", argc, argv);
+ return makeJsString(ctx, FileInfo::suffix(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_completeSuffix(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsCompleteSuffix(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("completeSuffix expects 1 argument"));
- }
- return FileInfo::completeSuffix(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.completeSuffix", argc, argv);
+ return makeJsString(ctx, FileInfo::completeSuffix(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_canonicalPath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsCanonicalPath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("canonicalPath expects 1 argument"));
- }
- return QFileInfo(context->argument(0).toString()).canonicalFilePath();
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.canonicalPath", argc, argv);
+ return makeJsString(ctx, QFileInfo(filePath).canonicalFilePath());
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_cleanPath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsCleanPath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("cleanPath expects 1 argument"));
- }
- return QDir::cleanPath(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.cleanPath", argc, argv);
+ return makeJsString(ctx, QDir::cleanPath(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_completeBaseName(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsCompleteBaseName(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("completeBaseName expects 1 argument"));
- }
- return FileInfo::completeBaseName(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.completeBaseName", argc, argv);
+ return makeJsString(ctx, FileInfo::completeBaseName(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_relativePath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsRelativePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("relativePath expects 2 arguments"));
- }
- const QString baseDir = context->argument(0).toString();
- const QString filePath = context->argument(1).toString();
- if (!FileInfo::isAbsolute(baseDir)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("FileInfo.relativePath() expects an absolute path as "
- "its first argument, but it is '%1'.").arg(baseDir));
- }
- if (!FileInfo::isAbsolute(filePath)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("FileInfo.relativePath() expects an absolute path as "
- "its second argument, but it is '%1'.").arg(filePath));
- }
- return QDir(baseDir).relativeFilePath(filePath);
+ try {
+ const auto args = getArguments<QString, QString>(ctx, "File.relativePath", argc, argv);
+ const QString baseDir = std::get<0>(args);
+ const QString filePath = std::get<1>(args);
+ if (!FileInfo::isAbsolute(baseDir)) {
+ throw Tr::tr("FileInfo.relativePath() expects an absolute path as "
+ "its first argument, but it is '%1'.").arg(baseDir);
+ }
+ if (!FileInfo::isAbsolute(filePath)) {
+ throw Tr::tr("FileInfo.relativePath() expects an absolute path as "
+ "its second argument, but it is '%1'.").arg(filePath);
+ }
+ return makeJsString(ctx, QDir(baseDir).relativeFilePath(filePath));
+
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_resolvePath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsResolvePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("resolvePath expects 2 arguments"));
- }
- const QString base = context->argument(0).toString();
- const QString rel = context->argument(1).toString();
- return FileInfo::resolvePath(base, rel);
+ try {
+ const auto args = getArguments<QString, QString>(ctx, "File.resolvePath", argc, argv);
+ const QString base = std::get<0>(args);
+ const QString rel = std::get<1>(args);
+ return makeJsString(ctx, FileInfo::resolvePath(base, rel));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_isAbsolutePath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsIsAbsolutePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("isAbsolutePath expects 1 argument"));
- }
- HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
- if (context->argumentCount() > 1) {
- hostOs = context->argument(1).toVariant().toStringList().contains(QLatin1String("windows"))
- ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
- }
- return FileInfo::isAbsolute(context->argument(0).toString(), hostOs);
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.isAbsolutePath", argc, argv);
+ HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
+ if (argc > 1) {
+ const auto osList = fromArg<QStringList>(ctx, "FileInfo.isAbsolutePath", 2, argv[1]);
+ hostOs = osList.contains(QLatin1String("windows"))
+ ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
+ }
+ return JS_NewBool(ctx, FileInfo::isAbsolute(filePath, hostOs));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_toWindowsSeparators(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsToWindowsSeparators(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("toWindowsSeparators expects 1 argument"));
- }
- return context->argument(0).toString().replace(QLatin1Char('/'), QLatin1Char('\\'));
+ try {
+ auto filePath = getArgument<QString>(ctx, "FileInfo.toWindowsSeparators", argc, argv);
+ return makeJsString(ctx, filePath.replace(QLatin1Char('/'), QLatin1Char('\\')));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_fromWindowsSeparators(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsFromWindowsSeparators(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("fromWindowsSeparators expects 1 argument"));
- }
- return context->argument(0).toString().replace(QLatin1Char('\\'), QLatin1Char('/'));
+ try {
+ auto filePath = getArgument<QString>(ctx, "FileInfo.fromWindowsSeparators", argc, argv);
+ return makeJsString(ctx, filePath.replace(QLatin1Char('\\'), QLatin1Char('/')));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_toNativeSeparators(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsToNativeSeparators(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("toNativeSeparators expects 1 argument"));
- }
- return QDir::toNativeSeparators(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.toWindowsSeparators", argc, argv);
+ return makeJsString(ctx, QDir::toNativeSeparators(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_fromNativeSeparators(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsFromNativeSeparators(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("fromNativeSeparators expects 1 argument"));
- }
- return QDir::fromNativeSeparators(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.fromWindowsSeparators",
+ argc, argv);
+ return makeJsString(ctx, QDir::fromNativeSeparators(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_joinPaths(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsJoinPaths(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- QStringList paths;
- for (int i = 0; i < context->argumentCount(); ++i) {
- const QScriptValue value = context->argument(i);
- if (!value.isUndefined() && !value.isNull()) {
- const QString arg = value.toString();
- if (!arg.isEmpty())
- paths.push_back(arg);
+ try {
+ QStringList paths;
+ for (int i = 0; i < argc; ++i) {
+ const auto value = fromArg<QString>(ctx, "FileInfo.joinPaths", i + 1, argv[i]);
+ if (!value.isEmpty())
+ paths.push_back(value);
}
- }
- return engine->toScriptValue(uniqueSeparators(paths.join(QLatin1Char('/'))));
+ return makeJsString(ctx, uniqueSeparators(paths.join(QLatin1Char('/'))));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_pathListSeparator(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsPathListSeparator(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
- return QString(HostOsInfo::pathListSeparator());
+ return makeJsString(ctx, QString(HostOsInfo::pathListSeparator()));
}
-QScriptValue FileInfoExtension::js_pathSeparator(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsPathSeparator(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
- return QString(HostOsInfo::pathSeparator());
+ return makeJsString(ctx, QString(HostOsInfo::pathSeparator()));
}
-QScriptValue FileInfoExtension::js_executableSuffix(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsExecutableSuffix(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
static QString executableSuffix = HostOsInfo::isWindowsHost() ?
- QLatin1String(QBS_HOST_EXE_SUFFIX) : QString();
- return executableSuffix;
+ QLatin1String(QBS_HOST_EXE_SUFFIX) : QString();
+ return makeJsString(ctx, executableSuffix);
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionFileInfo(QScriptValue extensionObject)
+void initializeJsExtensionFileInfo(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue fileInfoObj = engine->newQMetaObject(&FileInfoExtension::staticMetaObject,
- engine->newFunction(&FileInfoExtension::js_ctor));
- fileInfoObj.setProperty(StringConstants::fileInfoPath(),
- engine->newFunction(FileInfoExtension::js_path));
- fileInfoObj.setProperty(StringConstants::fileInfoFileName(),
- engine->newFunction(FileInfoExtension::js_fileName));
- fileInfoObj.setProperty(StringConstants::baseNameProperty(),
- engine->newFunction(FileInfoExtension::js_baseName));
- fileInfoObj.setProperty(QStringLiteral("suffix"),
- engine->newFunction(FileInfoExtension::js_suffix));
- fileInfoObj.setProperty(QStringLiteral("completeSuffix"),
- engine->newFunction(FileInfoExtension::js_completeSuffix));
- fileInfoObj.setProperty(QStringLiteral("canonicalPath"),
- engine->newFunction(FileInfoExtension::js_canonicalPath));
- fileInfoObj.setProperty(QStringLiteral("cleanPath"),
- engine->newFunction(FileInfoExtension::js_cleanPath));
- fileInfoObj.setProperty(StringConstants::completeBaseNameProperty(),
- engine->newFunction(FileInfoExtension::js_completeBaseName));
- fileInfoObj.setProperty(QStringLiteral("relativePath"),
- engine->newFunction(FileInfoExtension::js_relativePath));
- fileInfoObj.setProperty(QStringLiteral("resolvePath"),
- engine->newFunction(FileInfoExtension::js_resolvePath));
- fileInfoObj.setProperty(QStringLiteral("isAbsolutePath"),
- engine->newFunction(FileInfoExtension::js_isAbsolutePath));
- fileInfoObj.setProperty(QStringLiteral("toWindowsSeparators"),
- engine->newFunction(FileInfoExtension::js_toWindowsSeparators));
- fileInfoObj.setProperty(QStringLiteral("fromWindowsSeparators"),
- engine->newFunction(FileInfoExtension::js_fromWindowsSeparators));
- fileInfoObj.setProperty(QStringLiteral("toNativeSeparators"),
- engine->newFunction(FileInfoExtension::js_toNativeSeparators));
- fileInfoObj.setProperty(QStringLiteral("fromNativeSeparators"),
- engine->newFunction(FileInfoExtension::js_fromNativeSeparators));
- fileInfoObj.setProperty(QStringLiteral("joinPaths"),
- engine->newFunction(FileInfoExtension::js_joinPaths));
- fileInfoObj.setProperty(QStringLiteral("pathListSeparator"),
- engine->newFunction(FileInfoExtension::js_pathListSeparator));
- fileInfoObj.setProperty(QStringLiteral("pathSeparator"),
- engine->newFunction(FileInfoExtension::js_pathSeparator));
- fileInfoObj.setProperty(QStringLiteral("executableSuffix"),
- engine->newFunction(FileInfoExtension::js_executableSuffix));
- extensionObject.setProperty(QStringLiteral("FileInfo"), fileInfoObj);
+ qbs::Internal::FileInfoExtension::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::FileInfoExtension *)
-
-#include "fileinfoextension.moc"
diff --git a/src/lib/corelib/jsextensions/host.cpp b/src/lib/corelib/jsextensions/host.cpp
index ebf302a82..24d40fe6a 100644
--- a/src/lib/corelib/jsextensions/host.cpp
+++ b/src/lib/corelib/jsextensions/host.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/fileinfo.h>
@@ -44,27 +46,28 @@
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
-class Host : public QObject, QScriptable
+class Host : public JsExtension<Host>
{
- Q_OBJECT
public:
- static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_architecture(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_os(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_platform(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_osVersion(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_osBuildVersion(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_osVersionParts(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_osVersionMajor(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_osVersionMinor(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_osVersionPatch(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_nullDevice(QScriptContext *context, QScriptEngine *engine);
+ static const char *name() { return "Host"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static JSValue jsArchitecture(JSContext *ctx, JSValueConst, int, JSValueConst *) {
+ return makeJsString(ctx, HostOsInfo::hostOSArchitecture());
+ }
+ static JSValue jsOs(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsPlatform(JSContext *ctx, JSValueConst, int, JSValueConst *) {
+ return makeJsString(ctx, HostOsInfo::hostOSIdentifier());
+ }
+ static JSValue jsOsVersion(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsBuildVersion(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsVersionParts(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsVersionMajor(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsVersionMinor(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsVersionPatch(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsNullDevice(JSContext *ctx, JSValueConst, int, JSValueConst *);
};
static QStringList osList()
@@ -75,119 +78,74 @@ static QStringList osList()
return list;
}
-QScriptValue Host::js_ctor(QScriptContext *context, QScriptEngine *engine)
-{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'Host' cannot be instantiated."));
-}
-
-QScriptValue Host::js_architecture(QScriptContext *context, QScriptEngine *engine)
+void Host::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
-
- return HostOsInfo::hostOSArchitecture();
+ setupMethod(ctx, classObj, "architecture", &Host::jsArchitecture, 0);
+ setupMethod(ctx, classObj, "os", &Host::jsOs, 0);
+ setupMethod(ctx, classObj, "platform", &Host::jsPlatform, 0);
+ setupMethod(ctx, classObj, "osVersion", &Host::jsOsVersion, 0);
+ setupMethod(ctx, classObj, "osBuildVersion", &Host::jsOsBuildVersion, 0);
+ setupMethod(ctx, classObj, "osVersionParts", &Host::jsOsVersionParts, 0);
+ setupMethod(ctx, classObj, "osVersionMajor", &Host::jsOsVersionMajor, 0);
+ setupMethod(ctx, classObj, "osVersionMinor", &Host::jsOsVersionMinor, 0);
+ setupMethod(ctx, classObj, "osVersionPatch", &Host::jsOsVersionPatch, 0);
+ setupMethod(ctx, classObj, "nullDevice", &Host::jsNullDevice, 0);
}
-QScriptValue Host::js_os(QScriptContext *context, QScriptEngine *engine)
+JSValue Host::jsOs(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
static QStringList host = osList();
- return engine->toScriptValue(host);
+ return makeJsStringList(ctx, host);
}
-QScriptValue Host::js_platform(QScriptContext *context, QScriptEngine *engine)
+JSValue Host::jsOsVersion(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
- return HostOsInfo::hostOSIdentifier();
-}
-
-QScriptValue Host::js_osVersion(QScriptContext *context, QScriptEngine *engine)
-{
- Q_UNUSED(context);
- Q_UNUSED(engine);
static QString osVersion = HostOsInfo::hostOsVersion().toString();
- return osVersion.isNull() ? engine->undefinedValue() : osVersion;
+ return osVersion.isNull() ? JS_UNDEFINED : makeJsString(ctx, osVersion);
}
-QScriptValue Host::js_osBuildVersion(QScriptContext *context, QScriptEngine *engine)
+JSValue Host::jsOsBuildVersion(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
static QString osBuildVersion = HostOsInfo::hostOsBuildVersion();
- return osBuildVersion.isNull() ? engine->undefinedValue() : osBuildVersion;
+ return osBuildVersion.isNull() ? JS_UNDEFINED : makeJsString(ctx, osBuildVersion);
}
-QScriptValue Host::js_osVersionParts(QScriptContext *context, QScriptEngine *engine)
+JSValue Host::jsOsVersionParts(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
static QStringList osVersionParts = HostOsInfo::hostOsVersion().toString().split(
QStringLiteral("."));
- return qScriptValueFromSequence(engine, osVersionParts);
+ return makeJsStringList(ctx, osVersionParts);
}
-QScriptValue Host::js_osVersionMajor(QScriptContext *context, QScriptEngine *engine)
+JSValue Host::jsOsVersionMajor(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
static int osVersionMajor = HostOsInfo::hostOsVersion().majorVersion();
- return osVersionMajor;
+ return JS_NewInt32(ctx, osVersionMajor);
}
-QScriptValue Host::js_osVersionMinor(QScriptContext *context, QScriptEngine *engine)
+JSValue Host::jsOsVersionMinor(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
static int osVersionMinor = HostOsInfo::hostOsVersion().minorVersion();
- return osVersionMinor;
+ return JS_NewInt32(ctx, osVersionMinor);
}
-QScriptValue Host::js_osVersionPatch(QScriptContext *context, QScriptEngine *engine)
+JSValue Host::jsOsVersionPatch(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
static int osVersionPatch = HostOsInfo::hostOsVersion().patchLevel();
- return osVersionPatch;
+ return JS_NewInt32(ctx, osVersionPatch);
}
-QScriptValue Host::js_nullDevice(QScriptContext *context, QScriptEngine *engine)
+JSValue Host::jsNullDevice(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- Q_UNUSED(engine);
static QString nullDevice = HostOsInfo::isWindowsHost() ? QStringLiteral("NUL") :
QStringLiteral("/dev/null");
- return nullDevice;
+ return makeJsString(ctx, nullDevice);
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionHost(QScriptValue extensionObject)
+void initializeJsExtensionHost(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue hostObj = engine->newQMetaObject(&Host::staticMetaObject,
- engine->newFunction(&Host::js_ctor));
- hostObj.setProperty(QStringLiteral("architecture"), engine->newFunction(Host::js_architecture));
- hostObj.setProperty(QStringLiteral("os"), engine->newFunction(Host::js_os));
- hostObj.setProperty(QStringLiteral("platform"), engine->newFunction(Host::js_platform));
- hostObj.setProperty(QStringLiteral("osVersion"), engine->newFunction(Host::js_osVersion));
- hostObj.setProperty(QStringLiteral("osBuildVersion"), engine->newFunction(
- Host::js_osBuildVersion));
- hostObj.setProperty(QStringLiteral("osVersionParts"), engine->newFunction(
- Host::js_osVersionParts));
- hostObj.setProperty(QStringLiteral("osVersionMajor"), engine->newFunction(
- Host::js_osVersionMajor));
- hostObj.setProperty(QStringLiteral("osVersionMinor"), engine->newFunction(
- Host::js_osVersionMinor));
- hostObj.setProperty(QStringLiteral("osVersionPatch"), engine->newFunction(
- Host::js_osVersionPatch));
- hostObj.setProperty(QStringLiteral("nullDevice"), engine->newFunction(Host::js_nullDevice));
- extensionObject.setProperty(QStringLiteral("Host"), hostObj);
+ qbs::Internal::Host::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::Host *)
-
-#include "host.moc"
diff --git a/src/lib/corelib/jsextensions/jsextension.h b/src/lib/corelib/jsextensions/jsextension.h
new file mode 100644
index 000000000..e34c8ff83
--- /dev/null
+++ b/src/lib/corelib/jsextensions/jsextension.h
@@ -0,0 +1,337 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+
+#include <optional>
+#include <tuple>
+#include <utility>
+
+namespace qbs::Internal {
+
+template<typename ...T> struct PackHelper {};
+template<typename T> struct FromArgHelper;
+template <typename T> struct FunctionTrait;
+
+#define DECLARE_ENUM(ctx, obj, enumVal) \
+ JS_SetPropertyStr((ctx), (obj), #enumVal, JS_NewInt32((ctx), (enumVal)))
+
+template<class Derived> class JsExtension
+{
+#define DEFINE_JS_FORWARDER(name, func, text) \
+ static JSValue name(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) \
+ { \
+ return jsForward(func, text, ctx, this_val, argc, argv); \
+ }
+
+#define DEFINE_JS_FORWARDER_QUAL(klass, name, func, text) \
+ static JSValue name(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) \
+ { \
+ return klass::jsForward(func, text, ctx, this_val, argc, argv); \
+ }
+
+public:
+ virtual ~JsExtension() = default;
+
+ template<typename ...Args > static JSValue createObject(JSContext *ctx, Args... args)
+ {
+ ScopedJsValue obj(ctx, JS_NewObjectClass(ctx, classId(ctx)));
+ JS_SetOpaque(obj, new Derived(ctx, args...));
+ Derived::setupMethods(ctx, obj);
+ return obj.release();
+ }
+
+ static JSValue createObjectDirect(JSContext *ctx, void *obj)
+ {
+ JSValue jsObj = JS_NewObjectClass(ctx, classId(ctx));
+ attachPointerTo(jsObj, obj);
+ Derived::setupMethods(ctx, jsObj);
+ return jsObj;
+ }
+
+ static Derived *fromJsObject(JSContext *ctx, JSValue obj)
+ {
+ return attachedPointer<Derived>(obj, classId(ctx));
+ }
+
+ static void registerClass(ScriptEngine *engine, JSValue extensionObject)
+ {
+ if (const JSValue cachedValue = engine->getInternalExtension(Derived::name());
+ !JS_IsUndefined(cachedValue)) {
+ JS_SetPropertyStr(engine->context(), extensionObject, Derived::name(), cachedValue);
+ return;
+ }
+ engine->registerClass(Derived::name(), &Derived::ctor, &finalizer, extensionObject);
+ const ScopedJsValue classObj(
+ engine->context(),
+ JS_GetPropertyStr(engine->context(), extensionObject, Derived::name()));
+ Derived::setupStaticMethods(engine->context(), classObj);
+ Derived::declareEnums(engine->context(), classObj);
+ }
+
+ // obj is either class (for "static" methods) or class instance
+ template<typename Func> static void setupMethod(JSContext *ctx, JSValue obj, const char *name,
+ Func func, int argc)
+ {
+ JS_DefinePropertyValueStr(ctx, obj, name, JS_NewCFunction(ctx, func, name, argc), 0);
+ }
+
+ template<typename Func> static void setupMethod(JSContext *ctx, JSValue obj,
+ const QString &name, Func func, int argc)
+ {
+ setupMethod(ctx, obj, name.toUtf8().constData(), func, argc);
+ }
+
+ template<typename T> static T fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v)
+ {
+ return FromArgHelper<T>::fromArg(ctx, funcName, pos, v);
+ }
+
+ template <typename Tuple, std::size_t... I>
+ static void assignToTuple(Tuple &tuple, std::index_sequence<I...>, JSContext *ctx, const char *funcName, JSValueConst *argv) {
+ Q_UNUSED(ctx)
+ Q_UNUSED(funcName)
+ Q_UNUSED(argv)
+ ((std::get<I>(tuple) = fromArg<std::tuple_element_t<I, Tuple>>(ctx, funcName, I + 1, argv[I])), ...);
+ }
+
+ template<typename ...Args> static std::tuple<Args...> getArguments(JSContext *ctx,
+ const char *funcName, int argc, JSValueConst *argv)
+ {
+ constexpr int expectedArgc = sizeof ...(Args);
+ if (argc < expectedArgc) {
+ throw Tr::tr("%1 requires %d arguments.")
+ .arg(QLatin1String(funcName)).arg(expectedArgc);
+ }
+ std::tuple<std::remove_const_t<std::remove_reference_t<Args>>...> values;
+ assignToTuple(values, std::make_index_sequence<std::tuple_size<decltype(values)>::value>(),
+ ctx, funcName, argv);
+ return values;
+ }
+ template<typename ...Args> static std::tuple<Args...> getArgumentsHelper(PackHelper<Args...>, JSContext *ctx,
+ const char *funcName, int argc, JSValueConst *argv)
+ {
+ return getArguments<Args...>(ctx, funcName, argc, argv);
+ }
+
+ template<typename T> static T getArgument(JSContext *ctx, const char *funcName,
+ int argc, JSValueConst *argv)
+ {
+ return std::get<0>(getArguments<T>(ctx, funcName, argc, argv));
+ }
+
+ template <typename Func, typename Tuple, std::size_t... I>
+ static void jsForwardHelperVoid(Derived *obj, Func func, const Tuple &tuple, std::index_sequence<I...>) {
+ (obj->*func)(std::get<I>(tuple)...);
+ }
+ template <typename Func, typename Ret, typename Tuple, std::size_t... I>
+ static Ret jsForwardHelperRet(Derived *obj, Func &func, Tuple const& tuple, std::index_sequence<I...>) {
+ return (obj->*func)(std::get<I>(tuple)...);
+ }
+
+ static JSValue toJsValue(JSContext *ctx, const QString &s) { return makeJsString(ctx, s); }
+ static JSValue toJsValue(JSContext *ctx, bool v) { return JS_NewBool(ctx, v); }
+ static JSValue toJsValue(JSContext *ctx, qint32 v) { return JS_NewInt32(ctx, v); }
+ static JSValue toJsValue(JSContext *ctx, qint64 v) { return JS_NewInt64(ctx, v); }
+ static JSValue toJsValue(JSContext *ctx, const QVariant &v) { return makeJsVariant(ctx, v); }
+ static JSValue toJsValue(JSContext *ctx, const QByteArray &data)
+ {
+ const JSValue array = JS_NewArray(ctx);
+ for (int i = 0; i < data.size(); ++i)
+ JS_SetPropertyUint32(ctx, array, i, JS_NewInt32(ctx, data.at(i)));
+ return array;
+ }
+ static JSValue toJsValue(JSContext *ctx, const QVariantMap &m)
+ {
+ return makeJsVariantMap(ctx, m);
+ }
+ template<typename T> static JSValue toJsValue(JSContext *ctx, const std::optional<T> &v)
+ {
+ if (!v)
+ return JS_UNDEFINED;
+ return makeJsString(ctx, *v);
+ }
+
+ template<typename Func>
+ static JSValue jsForward(Func func, const char *name, JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ try {
+ using Ret = typename FunctionTrait<Func>::Ret;
+ using Args = typename FunctionTrait<Func>::Args;
+ const auto args = getArgumentsHelper(Args(), ctx, name, argc, argv);
+ const auto obj = fromJsObject(ctx, this_val);
+ if constexpr (std::is_same_v<Ret, void>) {
+ jsForwardHelperVoid(obj, func, args, std::make_index_sequence<std::tuple_size<decltype(args)>::value>());
+ return JS_UNDEFINED;
+ } else {
+ return toJsValue(ctx, jsForwardHelperRet<Func, Ret>(obj, func, args, std::make_index_sequence<std::tuple_size_v<decltype(args)>>()));
+ }
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+ }
+
+ static void setupMethods(JSContext *, JSValue) {}
+ static void setupStaticMethods(JSContext *, JSValue) {}
+ static void declareEnums(JSContext *, JSValue) {}
+
+private:
+ static void finalizer(JSRuntime *rt, JSValue val)
+ {
+ delete attachedPointer<Derived>(val, classId(rt));
+ }
+
+ static JSClassID classId(JSContext *ctx)
+ {
+ return ScriptEngine::engineForContext(ctx)->getClassId(Derived::name());
+ }
+
+ static JSClassID classId(JSRuntime *rt)
+ {
+ return ScriptEngine::engineForRuntime(rt)->getClassId(Derived::name());
+ }
+
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int)
+ {
+ return throwError(ctx, Tr::tr("'%1' cannot be instantiated.")
+ .arg(QLatin1String(Derived::name())));
+ }
+};
+
+template<> struct FromArgHelper<bool> {
+ static bool fromArg(JSContext *ctx, const char *, int, JSValue v) {
+ return JS_ToBool(ctx, v);
+ }
+};
+template<> struct FromArgHelper<qint32> {
+ static qint32 fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ int32_t val;
+ if (JS_ToInt32(ctx, &val, v) < 0) {
+ throw Tr::tr("%1 requires an integer as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return val;
+ }
+};
+template<> struct FromArgHelper<qint64> {
+ static qint64 fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ int64_t val;
+ if (JS_ToInt64(ctx, &val, v) < 0) {
+ throw Tr::tr("%1 requires an integer as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return val;
+ }
+};
+template<> struct FromArgHelper<QByteArray> {
+ static QByteArray fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ const auto throwError = [&] {
+ throw Tr::tr("%1 requires an array of bytes as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ };
+ if (!JS_IsArray(ctx, v))
+ throwError();
+ QByteArray data;
+ data.resize(getJsIntProperty(ctx, v, QLatin1String("length")));
+ for (int i = 0; i < data.size(); ++i) {
+ const JSValue jsNumber = JS_GetPropertyUint32(ctx, v, i);
+ if (JS_VALUE_GET_TAG(jsNumber) != JS_TAG_INT)
+ throwError();
+ int32_t n;
+ JS_ToInt32(ctx, &n, jsNumber);
+ if (n < 0 || n > 0xff)
+ throwError();
+ data[i] = n;
+ }
+ return data;
+ }
+};
+template<> struct FromArgHelper<QString> {
+ static QString fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ if (JS_IsUndefined(v))
+ return {};
+ const ScopedJsValue stringified(ctx, JS_ToString(ctx, v));
+ if (!JS_IsString(stringified)) {
+ throw Tr::tr("%1 requires a string as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return getJsString(ctx, stringified);
+ }
+};
+template<> struct FromArgHelper<QStringList> {
+ static QStringList fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ if (!JS_IsArray(ctx, v)) {
+ throw Tr::tr("%1 requires an array of strings as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return getJsStringList(ctx, v);
+ }
+};
+template<> struct FromArgHelper<QVariant> {
+ static QVariant fromArg(JSContext *ctx, const char *, int, JSValue v) {
+ return getJsVariant(ctx, v);
+ }
+};
+template<class C> struct FromArgHelper<C *> {
+ static C *fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ C * const obj = JsExtension<C>::fromJsObject(ctx, v);
+ if (!obj) {
+ throw Tr::tr("Argument %2 has wrong type in call to %1")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return obj;
+ }
+};
+
+template <typename R, typename C, typename... A>
+struct FunctionTrait<R(C::*)(A... )> {
+ using Ret = R;
+ using Args = PackHelper<std::remove_reference_t<A>...>;
+};
+template <typename R, typename C, typename... A>
+struct FunctionTrait<R(C::*)(A... ) const> {
+ using Ret = R;
+ using Args = PackHelper<std::remove_reference_t<A>...>;
+};
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/jsextensions/jsextensions.cpp b/src/lib/corelib/jsextensions/jsextensions.cpp
index 30dac8e8b..66d7ebbd6 100644
--- a/src/lib/corelib/jsextensions/jsextensions.cpp
+++ b/src/lib/corelib/jsextensions/jsextensions.cpp
@@ -39,17 +39,19 @@
#include "jsextensions.h"
+#include <language/scriptengine.h>
+#include <tools/scripttools.h>
+
#include <QtCore/qmap.h>
-#include <QtScript/qscriptengine.h>
#include <utility>
-using InitializerMap = QMap<QString, void (*)(QScriptValue)>;
+using InitializerMap = QMap<QString, void (*)(qbs::Internal::ScriptEngine *, JSValue)>;
static InitializerMap setupMap()
{
#define INITIALIZER_NAME(name) initializeJsExtension##name
#define ADD_JS_EXTENSION(name) \
- void INITIALIZER_NAME(name)(QScriptValue); \
+ void INITIALIZER_NAME(name)(qbs::Internal::ScriptEngine *, JSValue); \
map.insert(QStringLiteral(#name), &INITIALIZER_NAME(name))
InitializerMap map;
@@ -77,20 +79,21 @@ static InitializerMap &initializers()
return theMap;
}
-void JsExtensions::setupExtensions(const QStringList &names, const QScriptValue &scope)
+void JsExtensions::setupExtensions(ScriptEngine *engine, const QStringList &names,
+ const JSValue &scope)
{
for (const QString &name : names)
- initializers().value(name)(scope);
+ initializers().value(name)(engine, scope);
}
-QScriptValue JsExtensions::loadExtension(QScriptEngine *engine, const QString &name)
+JSValue JsExtensions::loadExtension(ScriptEngine *engine, const QString &name)
{
if (!hasExtension(name))
return {};
- QScriptValue extensionObj = engine->newObject();
- initializers().value(name)(extensionObj);
- return extensionObj.property(name);
+ ScopedJsValue extensionObj(engine->context(), engine->newObject());
+ initializers().value(name)(engine, extensionObj);
+ return getJsProperty(engine->context(), extensionObj, name);
}
bool JsExtensions::hasExtension(const QString &name)
diff --git a/src/lib/corelib/jsextensions/jsextensions.h b/src/lib/corelib/jsextensions/jsextensions.h
index f1ebfbdc3..2602679b5 100644
--- a/src/lib/corelib/jsextensions/jsextensions.h
+++ b/src/lib/corelib/jsextensions/jsextensions.h
@@ -40,22 +40,20 @@
#ifndef QBS_JSEXTENSIONS_H
#define QBS_JSEXTENSIONS_H
-#include <QtCore/qhash.h>
-#include <QtCore/qstringlist.h>
+#include <quickjs.h>
-QT_BEGIN_NAMESPACE
-class QScriptEngine;
-class QScriptValue;
-QT_END_NAMESPACE
+#include <QtCore/qstringlist.h>
namespace qbs {
namespace Internal {
+class ScriptEngine;
class JsExtensions
{
public:
- static void setupExtensions(const QStringList &names, const QScriptValue &scope);
- static QScriptValue loadExtension(QScriptEngine *engine, const QString &name);
+ static void setupExtensions(ScriptEngine *engine, const QStringList &names,
+ const JSValue &scope);
+ static JSValue loadExtension(ScriptEngine *engine, const QString &name);
static bool hasExtension(const QString &name);
static QStringList extensionNames();
};
diff --git a/src/lib/corelib/jsextensions/moduleproperties.cpp b/src/lib/corelib/jsextensions/moduleproperties.cpp
index f721e0016..4f83bf640 100644
--- a/src/lib/corelib/jsextensions/moduleproperties.cpp
+++ b/src/lib/corelib/jsextensions/moduleproperties.cpp
@@ -41,8 +41,8 @@
#include <buildgraph/artifact.h>
#include <buildgraph/artifactsscriptvalue.h>
+#include <buildgraph/buildgraph.h>
#include <buildgraph/dependencyparametersscriptvalue.h>
-#include <buildgraph/scriptclasspropertyiterator.h>
#include <language/language.h>
#include <language/propertymapinternal.h>
#include <language/qualifiedid.h>
@@ -53,28 +53,21 @@
#include <tools/qttools.h>
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
-
-#include <QtScript/qscriptclass.h>
+#include <utility>
namespace qbs {
namespace Internal {
-QScriptValue getDataForModuleScriptValue(QScriptEngine *engine, const ResolvedProduct *product,
- const Artifact *artifact, const ResolvedModule *module)
+JSValue createDataForModuleScriptValue(ScriptEngine *engine, const Artifact *artifact)
{
- QScriptValue data = engine->newObject();
- data.setProperty(ModuleNameKey, module->name);
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(product));
- data.setProperty(ProductPtrKey, engine->newVariant(v));
- v.setValue<quintptr>(reinterpret_cast<quintptr>(artifact));
- data.setProperty(ArtifactPtrKey, engine->newVariant(v));
+ JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ attachPointerTo(data, artifact);
return data;
}
-static QScriptValue getModuleProperty(const ResolvedProduct *product, const Artifact *artifact,
- ScriptEngine *engine, const QString &moduleName,
- const QString &propertyName, bool *isPresent = nullptr)
+static JSValue getModuleProperty(const ResolvedProduct *product, const Artifact *artifact,
+ ScriptEngine *engine, const QString &moduleName,
+ const QString &propertyName, bool *isPresent = nullptr)
{
const PropertyMapConstPtr &properties = artifact ? artifact->properties
: product->moduleProperties;
@@ -84,7 +77,7 @@ static QScriptValue getModuleProperty(const ResolvedProduct *product, const Arti
if (!value.isValid()) {
value = properties->moduleProperty(moduleName, propertyName, isPresent);
- // Cache the variant value. We must not cache the QScriptValue here, because it's a
+ // Cache the variant value. We must not cache the script value here, because it's a
// reference and the user might change the actual object.
if (engine->isPropertyCacheEnabled())
engine->addToPropertyCache(moduleName, propertyName, properties, value);
@@ -102,111 +95,101 @@ static QScriptValue getModuleProperty(const ResolvedProduct *product, const Arti
return engine->toScriptValue(value);
}
-class ModulePropertyScriptClass : public QScriptClass
-{
-public:
- ModulePropertyScriptClass(QScriptEngine *engine)
- : QScriptClass(engine)
- {
- }
-
-private:
- QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name,
- QueryFlags flags, uint *id) override
- {
- Q_UNUSED(flags);
- Q_UNUSED(id);
-
- if (name == StringConstants::dependenciesProperty()
- || name == StringConstants::artifactsProperty()) {
- // The prototype is not backed by a QScriptClass.
- m_result = object.prototype().property(name);
- return HandlesReadAccess;
- }
-
- if (name == StringConstants::parametersProperty()) {
- m_result = object.data().property(DependencyParametersKey);
- return HandlesReadAccess;
- }
+struct ModuleData {
+ JSValue dependencyParameters = JS_UNDEFINED;
+ const Artifact *artifact = nullptr;
+};
- setup(object);
- QBS_ASSERT(m_product, return {});
- bool isPresent;
- m_result = getModuleProperty(m_product, m_artifact, static_cast<ScriptEngine *>(engine()),
- m_moduleName, name, &isPresent);
+ModuleData getModuleData(JSContext *ctx, JSValue obj)
+{
+ const ScopedJsValue jsData(ctx, getJsProperty(ctx, obj,
+ StringConstants::dataPropertyInternal()));
+ ModuleData data;
+ data.dependencyParameters = JS_GetPropertyUint32(ctx, jsData, DependencyParametersKey);
+ data.artifact = attachedPointer<Artifact>(
+ jsData, ScriptEngine::engineForContext(ctx)->dataWithPtrClass());
+ return data;
+}
- // It is important that we reject unknown property names. Otherwise QtScript will forward
- // *everything* to us, including built-in stuff like the hasOwnProperty function.
- return isPresent ? HandlesReadAccess : QueryFlags();
+static int getModulePropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj)
+{
+ const auto engine = ScriptEngine::engineForContext(ctx);
+ const ModuleData data = getModuleData(ctx, obj);
+ const auto module = attachedPointer<ResolvedModule>(obj, engine->modulePropertyScriptClass());
+ QBS_ASSERT(module, return -1);
+
+ const PropertyMapInternal *propertyMap;
+ QStringList additionalProperties;
+ if (data.artifact) {
+ propertyMap = data.artifact->properties.get();
+ } else {
+ propertyMap = module->product->moduleProperties.get();
+ if (JS_IsObject(data.dependencyParameters))
+ additionalProperties.push_back(StringConstants::parametersProperty());
}
+ JS_FreeValue(ctx, data.dependencyParameters);
+ getPropertyNames(ctx, ptab, plen, propertyMap->value().value(module->name).toMap(),
+ additionalProperties, engine->baseModuleScriptValue(module));
+ return 0;
+}
- QScriptValue property(const QScriptValue &, const QScriptString &, uint) override
- {
- return m_result;
+static int getModuleProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj,
+ JSAtom prop)
+{
+ if (desc) {
+ desc->getter = desc->setter = desc->value = JS_UNDEFINED;
+ desc->flags = JS_PROP_ENUMERABLE;
}
-
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override
- {
- setup(object);
- QBS_ASSERT(m_artifact || m_product, return nullptr);
- const PropertyMapInternal *propertyMap;
- std::vector<QString> additionalProperties({StringConstants::artifactsProperty(),
- StringConstants::dependenciesProperty()});
- if (m_artifact) {
- propertyMap = m_artifact->properties.get();
- } else {
- propertyMap = m_product->moduleProperties.get();
- if (object.data().property(DependencyParametersKey).isValid())
- additionalProperties.push_back(StringConstants::parametersProperty());
- }
- return new ScriptClassPropertyIterator(object,
- propertyMap->value().value(m_moduleName).toMap(),
- additionalProperties);
+ const auto engine = ScriptEngine::engineForContext(ctx);
+ const QString name = getJsString(ctx, prop);
+ const ModuleData data = getModuleData(ctx, obj);
+ const auto module = attachedPointer<ResolvedModule>(obj, engine->modulePropertyScriptClass());
+ QBS_ASSERT(module, return -1);
+
+ ScopedJsValue parametersMgr(ctx, data.dependencyParameters);
+ if (name == StringConstants::parametersProperty()) {
+ if (desc)
+ desc->value = parametersMgr.release();
+ return 1;
}
- void setup(const QScriptValue &object)
- {
- if (m_lastObjectId != object.objectId()) {
- m_lastObjectId = object.objectId();
- const QScriptValue data = object.data();
- QBS_ASSERT(data.isValid(), return);
- m_moduleName = data.property(ModuleNameKey).toString();
- m_product = reinterpret_cast<const ResolvedProduct *>(
- data.property(ProductPtrKey).toVariant().value<quintptr>());
- m_artifact = reinterpret_cast<const Artifact *>(
- data.property(ArtifactPtrKey).toVariant().value<quintptr>());
- }
+ bool isPresent;
+ JSValue value = getModuleProperty(
+ module->product, data.artifact, ScriptEngine::engineForContext(ctx),
+ module->name, name, &isPresent);
+ if (isPresent) {
+ if (desc)
+ desc->value = value;
+ return 1;
}
- qint64 m_lastObjectId = 0;
- QString m_moduleName;
- const ResolvedProduct *m_product = nullptr;
- const Artifact *m_artifact = nullptr;
- QScriptValue m_result;
-};
-
-static QString ptrKey() { return QStringLiteral("__internalPtr"); }
-static QString typeKey() { return QStringLiteral("__type"); }
-static QString artifactType() { return QStringLiteral("artifact"); }
+ ScopedJsValue v(ctx, JS_GetProperty(ctx, engine->baseModuleScriptValue(module), prop));
+ const int ret = JS_IsUndefined(v) ? 0 : 1;
+ if (desc)
+ desc->value = v.release();
+ return ret;
+}
-static QScriptValue js_moduleDependencies(QScriptContext *, ScriptEngine *engine,
- const ResolvedModule *module)
+static JSValue js_moduleDependencies(JSContext *ctx, JSValueConst this_val, int , JSValueConst *)
{
- QScriptValue result = engine->newArray();
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto module = attachedPointer<ResolvedModule>(this_val, engine->dataWithPtrClass());
+ JSValue result = JS_NewArray(engine->context());
quint32 idx = 0;
for (const QString &depName : qAsConst(module->moduleDependencies)) {
for (const auto &dep : module->product->modules) {
if (dep->name != depName)
continue;
- QScriptValue obj = engine->newObject(engine->modulePropertyScriptClass());
- obj.setPrototype(engine->moduleScriptValuePrototype(dep.get()));
- QScriptValue data = getDataForModuleScriptValue(engine, module->product, nullptr,
- dep.get());
+ JSValue obj = JS_NewObjectClass(ctx, engine->modulePropertyScriptClass());
+ attachPointerTo(obj, dep.get());
+ JSValue data = createDataForModuleScriptValue(engine, nullptr);
const QVariantMap &params = module->product->moduleParameters.value(dep);
- data.setProperty(DependencyParametersKey, dependencyParametersValue(
- module->product->uniqueName(), dep->name, params, engine));
- obj.setData(data);
- result.setProperty(idx++, obj);
+ JS_SetPropertyUint32(ctx, data, DependencyParametersKey,
+ dependencyParametersValue(module->product->uniqueName(),
+ dep->name, params, engine));
+ defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
break;
}
}
@@ -214,35 +197,84 @@ static QScriptValue js_moduleDependencies(QScriptContext *, ScriptEngine *engine
return result;
}
-static QScriptValue setupModuleScriptValue(ScriptEngine *engine,
- const ResolvedModule *module)
+template<class ProductOrArtifact>
+static JSValue moduleProperty(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
+{
+ if (Q_UNLIKELY(argc < 2))
+ return throwError(ctx, Tr::tr("Function moduleProperty() expects 2 arguments"));
+
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const ResolvedProduct *product = nullptr;
+ const Artifact *artifact = nullptr;
+ if constexpr (std::is_same_v<ProductOrArtifact, ResolvedProduct>) {
+ product = attachedPointer<ResolvedProduct>(this_val, engine->productPropertyScriptClass());
+ } else {
+ artifact = attachedPointer<Artifact>(this_val, engine->dataWithPtrClass());
+ product = artifact->product;
+ }
+
+ const QString moduleName = getJsString(ctx, argv[0]);
+ const QString propertyName = getJsString(ctx, argv[1]);
+ return getModuleProperty(product, artifact, engine, moduleName, propertyName);
+}
+
+
+template<class ProductOrArtifact>
+static JSValue js_moduleProperty(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
+{
+ try {
+ return moduleProperty<ProductOrArtifact>(ctx, this_val, argc, argv);
+ } catch (const ErrorInfo &e) {
+ return throwError(ctx, e.toString());
+ }
+}
+
+template<class ProductOrArtifact>
+static void initModuleProperties(ScriptEngine *engine, JSValue objectWithProperties)
+{
+ JSContext * const ctx = engine->context();
+ JSValue func = JS_NewCFunction(ctx, js_moduleProperty<ProductOrArtifact>, "moduleProperty", 2);
+ setJsProperty(ctx, objectWithProperties, QStringLiteral("moduleProperty"), func);
+}
+
+static JSValue setupBaseModuleScriptValue(ScriptEngine *engine, const ResolvedModule *module)
{
- QScriptValue &moduleScriptValue = engine->moduleScriptValuePrototype(module);
- if (moduleScriptValue.isValid())
+ JSValue &moduleScriptValue = engine->baseModuleScriptValue(module);
+ if (JS_IsObject(moduleScriptValue))
return moduleScriptValue;
- moduleScriptValue = engine->newObject();
- QScriptValue depfunc = engine->newFunction<const ResolvedModule *>(&js_moduleDependencies,
- module);
- moduleScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
- QScriptValue artifactsFunc = engine->newFunction(&artifactsScriptValueForModule, module);
- moduleScriptValue.setProperty(StringConstants::artifactsProperty(), artifactsFunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
+ const ScopedJsValue proto(engine->context(), JS_NewObject(engine->context()));
+ moduleScriptValue = JS_NewObjectProtoClass(engine->context(), proto,
+ engine->dataWithPtrClass());
+ attachPointerTo(moduleScriptValue, module);
+ QByteArray name = StringConstants::dependenciesProperty().toUtf8();
+ const ScopedJsValue depfunc(
+ engine->context(),
+ JS_NewCFunction(engine->context(), js_moduleDependencies, name.constData(), 0));
+ const ScopedJsAtom depAtom(engine->context(), name);
+ JS_DefineProperty(engine->context(), moduleScriptValue, depAtom,
+ JS_UNDEFINED, depfunc, JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
+ name = StringConstants::artifactsProperty().toUtf8();
+ const ScopedJsValue artifactsFunc(
+ engine->context(),
+ JS_NewCFunction(engine->context(), &artifactsScriptValueForModule,
+ name.constData(), 0));
+ const ScopedJsAtom artifactsAtom(engine->context(), name);
+ JS_DefineProperty(engine->context(), moduleScriptValue, artifactsAtom,
+ JS_UNDEFINED, artifactsFunc, JS_UNDEFINED,
+ JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
return moduleScriptValue;
}
-void ModuleProperties::init(QScriptValue productObject,
+void ModuleProperties::init(ScriptEngine *engine, JSValue productObject,
const ResolvedProduct *product)
{
- init(productObject, product, StringConstants::productValue());
- setupModules(productObject, product, nullptr);
+ initModuleProperties<ResolvedProduct>(engine, productObject);
+ setupModules(engine, productObject, product, nullptr);
}
-void ModuleProperties::init(QScriptValue artifactObject, const Artifact *artifact)
+void ModuleProperties::init(ScriptEngine *engine, JSValue artifactObject, const Artifact *artifact)
{
- init(artifactObject, artifact, artifactType());
+ initModuleProperties<Artifact>(engine, artifactObject);
const auto product = artifact->product;
const QVariantMap productProperties {
{StringConstants::buildDirectoryProperty(), product->buildDirectory()},
@@ -252,106 +284,51 @@ void ModuleProperties::init(QScriptValue artifactObject, const Artifact *artifac
{StringConstants::targetNameProperty(), product->targetName},
{StringConstants::typeProperty(), sorted(product->fileTags.toStringList())}
};
- QScriptEngine * const engine = artifactObject.engine();
- artifactObject.setProperty(StringConstants::productVar(),
- engine->toScriptValue(productProperties));
- setupModules(artifactObject, artifact->product.get(), artifact);
+ setJsProperty(engine->context(), artifactObject, StringConstants::productVar(),
+ engine->toScriptValue(productProperties));
+ setupModules(engine, artifactObject, artifact->product.get(), artifact);
}
-void ModuleProperties::setModuleScriptValue(QScriptValue targetObject,
- const QScriptValue &moduleObject, const QString &moduleName)
+void ModuleProperties::setModuleScriptValue(ScriptEngine *engine, JSValue targetObject,
+ const JSValue &moduleObject, const QString &moduleName)
{
- auto const e = static_cast<ScriptEngine *>(targetObject.engine());
const QualifiedId name = QualifiedId::fromString(moduleName);
- QScriptValue obj = targetObject;
+ JSValue obj = targetObject;
for (int i = 0; i < name.size() - 1; ++i) {
- QScriptValue tmp = obj.property(name.at(i));
- if (!tmp.isObject())
- tmp = e->newObject();
- obj.setProperty(name.at(i), tmp);
+ JSValue tmp = getJsProperty(engine->context(), obj, name.at(i));
+ if (!JS_IsObject(tmp)) {
+ tmp = engine->newObject();
+ setJsProperty(engine->context(), obj, name.at(i), tmp);
+ } else {
+ JS_FreeValue(engine->context(), tmp);
+ }
obj = tmp;
}
- obj.setProperty(name.last(), moduleObject);
- if (moduleName.size() > 1)
- targetObject.setProperty(moduleName, moduleObject);
-}
-
-void ModuleProperties::init(QScriptValue objectWithProperties, const void *ptr,
- const QString &type)
-{
- QScriptEngine * const engine = objectWithProperties.engine();
- objectWithProperties.setProperty(QStringLiteral("moduleProperty"),
- engine->newFunction(ModuleProperties::js_moduleProperty, 2));
- objectWithProperties.setProperty(ptrKey(), engine->toScriptValue(quintptr(ptr)));
- objectWithProperties.setProperty(typeKey(), type);
+ setJsProperty(engine->context(), obj, name.last(), moduleObject);
+ if (name.size() > 1) {
+ setJsProperty(engine->context(), targetObject, moduleName,
+ JS_DupValue(engine->context(), moduleObject));
+ }
}
-void ModuleProperties::setupModules(QScriptValue &object, const ResolvedProduct *product,
- const Artifact *artifact)
+void ModuleProperties::setupModules(ScriptEngine *engine, JSValue &object,
+ const ResolvedProduct *product, const Artifact *artifact)
{
- const auto engine = static_cast<ScriptEngine *>(object.engine());
- QScriptClass *modulePropertyScriptClass = engine->modulePropertyScriptClass();
- if (!modulePropertyScriptClass) {
- modulePropertyScriptClass = new ModulePropertyScriptClass(engine);
+ JSClassID modulePropertyScriptClass = engine->modulePropertyScriptClass();
+ if (modulePropertyScriptClass == 0) {
+ modulePropertyScriptClass = engine->registerClass("ModulePropertyScriptClass", nullptr,
+ nullptr, JS_UNDEFINED, &getModulePropertyNames, &getModuleProperty);
engine->setModulePropertyScriptClass(modulePropertyScriptClass);
}
for (const auto &module : product->modules) {
- QScriptValue moduleObjectPrototype = setupModuleScriptValue(engine, module.get());
- QScriptValue moduleObject = engine->newObject(modulePropertyScriptClass);
- moduleObject.setPrototype(moduleObjectPrototype);
- moduleObject.setData(getDataForModuleScriptValue(engine, product, artifact, module.get()));
- setModuleScriptValue(object, moduleObject, module->name);
- }
-}
-
-QScriptValue ModuleProperties::js_moduleProperty(QScriptContext *context, QScriptEngine *engine)
-{
- try {
- return moduleProperty(context, engine);
- } catch (const ErrorInfo &e) {
- return context->throwError(e.toString());
+ setupBaseModuleScriptValue(engine, module.get());
+ JSValue moduleObject = JS_NewObjectClass(engine->context(), modulePropertyScriptClass);
+ attachPointerTo(moduleObject, module.get());
+ defineJsProperty(engine->context(), moduleObject, StringConstants::dataPropertyInternal(),
+ createDataForModuleScriptValue(engine, artifact));
+ setModuleScriptValue(engine, object, moduleObject, module->name);
}
}
-QScriptValue ModuleProperties::moduleProperty(QScriptContext *context, QScriptEngine *engine)
-{
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("Function moduleProperty() expects 2 arguments"));
- }
-
- const QScriptValue objectWithProperties = context->thisObject();
- const QScriptValue typeScriptValue = objectWithProperties.property(typeKey());
- if (Q_UNLIKELY(!typeScriptValue.isString())) {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: __type not set up"));
- }
- const QScriptValue ptrScriptValue = objectWithProperties.property(ptrKey());
- if (Q_UNLIKELY(!ptrScriptValue.isNumber())) {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: __internalPtr not set up"));
- }
-
- const void *ptr = reinterpret_cast<const void *>(qscriptvalue_cast<quintptr>(ptrScriptValue));
- const ResolvedProduct *product = nullptr;
- const Artifact *artifact = nullptr;
- if (typeScriptValue.toString() == StringConstants::productValue()) {
- QBS_ASSERT(ptr, return {});
- product = static_cast<const ResolvedProduct *>(ptr);
- } else if (typeScriptValue.toString() == artifactType()) {
- QBS_ASSERT(ptr, return {});
- artifact = static_cast<const Artifact *>(ptr);
- product = artifact->product.get();
- } else {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: invalid type"));
- }
-
- const auto qbsEngine = static_cast<ScriptEngine *>(engine);
- const QString moduleName = context->argument(0).toString();
- const QString propertyName = context->argument(1).toString();
- return getModuleProperty(product, artifact, qbsEngine, moduleName, propertyName);
-}
-
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/moduleproperties.h b/src/lib/corelib/jsextensions/moduleproperties.h
index 3fe4fbfd0..6272ee8a2 100644
--- a/src/lib/corelib/jsextensions/moduleproperties.h
+++ b/src/lib/corelib/jsextensions/moduleproperties.h
@@ -43,41 +43,32 @@
#include <buildgraph/forward_decls.h>
#include <language/forward_decls.h>
-#include <QtScript/qscriptcontext.h>
-#include <QtScript/qscriptvalue.h>
+#include <quickjs.h>
namespace qbs {
namespace Internal {
-
class ScriptEngine;
enum ModulePropertiesScriptValueCommonPropertyKeys : quint32
{
ModuleNameKey,
- ProductPtrKey,
- ArtifactPtrKey,
DependencyParametersKey,
};
-QScriptValue getDataForModuleScriptValue(QScriptEngine *engine, const ResolvedProduct *product,
- const Artifact *artifact, const ResolvedModule *module);
+JSValue createDataForModuleScriptValue(ScriptEngine *engine,
+ const Artifact *artifact);
class ModuleProperties
{
public:
- static void init(QScriptValue productObject, const ResolvedProduct *product);
- static void init(QScriptValue artifactObject, const Artifact *artifact);
- static void setModuleScriptValue(QScriptValue targetObject, const QScriptValue &moduleObject,
- const QString &moduleName);
+ static void init(ScriptEngine *engine, JSValue productObject, const ResolvedProduct *product);
+ static void init(ScriptEngine *engine, JSValue artifactObject, const Artifact *artifact);
+ static void setModuleScriptValue(ScriptEngine *engine, JSValue targetObject,
+ const JSValue &moduleObject, const QString &moduleName);
private:
- static void init(QScriptValue objectWithProperties, const void *ptr, const QString &type);
- static void setupModules(QScriptValue &object, const ResolvedProduct *product,
+ static void setupModules(ScriptEngine *engine, JSValue &object, const ResolvedProduct *product,
const Artifact *artifact);
-
- static QScriptValue js_moduleProperty(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue moduleProperty(QScriptContext *context, QScriptEngine *engine);
};
} // namespace Internal
diff --git a/src/lib/corelib/jsextensions/pkgconfigjs.cpp b/src/lib/corelib/jsextensions/pkgconfigjs.cpp
index 76cc35084..2d80ec770 100644
--- a/src/lib/corelib/jsextensions/pkgconfigjs.cpp
+++ b/src/lib/corelib/jsextensions/pkgconfigjs.cpp
@@ -42,9 +42,6 @@
#include <language/scriptengine.h>
#include <tools/version.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-
#include <QtCore/QProcessEnvironment>
#include <stdexcept>
@@ -186,35 +183,39 @@ std::vector<std::string> stringListToStdVector(const QStringList &list)
} // namespace
-QScriptValue PkgConfigJs::ctor(QScriptContext *context, QScriptEngine *engine)
+void PkgConfigJs::declareEnums(JSContext *ctx, JSValue classObj)
{
- try {
- PkgConfigJs *e = nullptr;
- switch (context->argumentCount()) {
- case 0:
- e = new PkgConfigJs(context, engine);
- break;
- case 1:
- e = new PkgConfigJs(context, engine, context->argument(0).toVariant().toMap());
- break;
-
- default:
- return context->throwError(
- QStringLiteral("TextFile constructor takes at most three parameters."));
- }
+ DECLARE_ENUM(ctx, classObj, LibraryName);
+ DECLARE_ENUM(ctx, classObj, LibraryPath);
+ DECLARE_ENUM(ctx, classObj, StaticLibraryName);
+ DECLARE_ENUM(ctx, classObj, Framework);
+ DECLARE_ENUM(ctx, classObj, FrameworkPath);
+ DECLARE_ENUM(ctx, classObj, LinkerFlag);
+ DECLARE_ENUM(ctx, classObj, IncludePath);
+ DECLARE_ENUM(ctx, classObj, SystemIncludePath);
+ DECLARE_ENUM(ctx, classObj, Define);
+ DECLARE_ENUM(ctx, classObj, CompilerFlag);
+}
- return engine->newQObject(e, QScriptEngine::ScriptOwnership);
+JSValue PkgConfigJs::ctor(JSContext *ctx, JSValue, JSValue, int argc, JSValue *argv, int)
+{
+ try {
+ QVariantMap options;
+ if (argc > 0)
+ options = fromArg<QVariant>(ctx, "PkgConfig constructor", 1, argv[0]).toMap();
+ JSValue obj = createObject(ctx, options);
+ return obj;
} catch (const PcException &e) {
- return context->throwError(QString::fromUtf8(e.what()));
+ return throwError(ctx, QString::fromUtf8(e.what()));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
}
-PkgConfigJs::PkgConfigJs(
- QScriptContext *context, QScriptEngine *engine, const QVariantMap &options) :
+PkgConfigJs::PkgConfigJs(JSContext *ctx, const QVariantMap &options) :
m_pkgConfig(std::make_unique<PkgConfig>(
- convertOptions(static_cast<ScriptEngine *>(engine)->environment(), options)))
+ convertOptions(ScriptEngine::engineForContext(ctx)->environment(), options)))
{
- Q_UNUSED(context);
for (const auto &package : m_pkgConfig->packages()) {
m_packages.insert(
QString::fromStdString(package.getBaseFileName()), packageVariantToMap(package));
@@ -249,16 +250,15 @@ PkgConfig::Options PkgConfigJs::convertOptions(const QProcessEnvironment &env, c
return result;
}
+void PkgConfigJs::setupMethods(JSContext *ctx, JSValue obj)
+{
+ setupMethod(ctx, obj, "packages", &PkgConfigJs::jsPackages, 0);
+}
+
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionPkgConfig(QScriptValue extensionObject)
+void initializeJsExtensionPkgConfig(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(
- &PkgConfigJs::staticMetaObject, engine->newFunction(&PkgConfigJs::ctor));
- extensionObject.setProperty(QStringLiteral("PkgConfig"), obj);
+ qbs::Internal::PkgConfigJs::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::PkgConfigJs *)
diff --git a/src/lib/corelib/jsextensions/pkgconfigjs.h b/src/lib/corelib/jsextensions/pkgconfigjs.h
index 462a8f2bd..7e84545a0 100644
--- a/src/lib/corelib/jsextensions/pkgconfigjs.h
+++ b/src/lib/corelib/jsextensions/pkgconfigjs.h
@@ -37,16 +37,15 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include "tools/qbs_export.h"
#include <tools/stlutils.h>
#include <pkgconfig.h>
-#include <QtCore/qobject.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptable.h>
-
#include <memory>
class QProcessEnvironment;
@@ -54,13 +53,10 @@ class QProcessEnvironment;
namespace qbs {
namespace Internal {
-class QBS_AUTOTEST_EXPORT PkgConfigJs : public QObject, QScriptable
+class QBS_AUTOTEST_EXPORT PkgConfigJs : public JsExtension<PkgConfigJs>
{
- Q_OBJECT
public:
-
- // can we trick moc here to avoid duplication?
- enum class FlagType {
+ enum FlagType {
LibraryName = toUnderlying(PcPackage::Flag::Type::LibraryName),
LibraryPath = toUnderlying(PcPackage::Flag::Type::LibraryPath),
StaticLibraryName = toUnderlying(PcPackage::Flag::Type::StaticLibraryName),
@@ -72,7 +68,6 @@ public:
Define = toUnderlying(PcPackage::Flag::Type::Define),
CompilerFlag = toUnderlying(PcPackage::Flag::Type::CompilerFlag),
};
- Q_ENUM(FlagType);
enum class ComparisonType {
LessThan,
@@ -83,18 +78,22 @@ public:
NotEqual,
AlwaysMatch
};
- Q_ENUM(ComparisonType);
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
+ static const char *name() { return "PkgConfig"; }
+ static void declareEnums(JSContext *ctx, JSValue classObj);
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int);
- explicit PkgConfigJs(
- QScriptContext *context, QScriptEngine *engine, const QVariantMap &options = {});
+ explicit PkgConfigJs(JSContext *ctx, const QVariantMap &options = {});
- Q_INVOKABLE QVariantMap packages() const { return m_packages; }
+ DEFINE_JS_FORWARDER(jsPackages, &PkgConfigJs::packages, "PkgConfig.packages")
+ QVariantMap packages() const { return m_packages; }
// also used in tests
static PkgConfig::Options convertOptions(const QProcessEnvironment &env, const QVariantMap &map);
+ static void setupMethods(JSContext *ctx, JSValue obj);
+
private:
std::unique_ptr<PkgConfig> m_pkgConfig;
QVariantMap m_packages;
diff --git a/src/lib/corelib/jsextensions/process.cpp b/src/lib/corelib/jsextensions/process.cpp
index f0c3c4705..f0febb364 100644
--- a/src/lib/corelib/jsextensions/process.cpp
+++ b/src/lib/corelib/jsextensions/process.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/executablefinder.h>
@@ -44,7 +46,6 @@
#include <tools/shellutils.h>
#include <tools/stringconstants.h>
-#include <QtCore/qobject.h>
#include <QtCore/qprocess.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qvariant.h>
@@ -53,133 +54,161 @@
#else
#include <QtCore/qtextcodec.h>
#endif
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
namespace qbs {
namespace Internal {
-class Process : public QObject, public QScriptable, public ResourceAcquiringScriptObject
+class Process : public JsExtension<Process>
{
- Q_OBJECT
+ friend class JsExtension<Process>;
public:
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
- Process(QScriptContext *context);
-
- Q_INVOKABLE QString getEnv(const QString &name);
- Q_INVOKABLE void setEnv(const QString &name, const QString &value);
- Q_INVOKABLE void setCodec(const QString &codec);
+ static const char *name() { return "Process"; }
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int);
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static void setupMethods(JSContext *ctx, JSValue obj);
+ Process(JSContext *context);
+
+ DEFINE_JS_FORWARDER(jsGetEnv, &Process::getEnv, "Process.getEnv")
+ DEFINE_JS_FORWARDER(jsSetEnv, &Process::setEnv, "Process.setEnv")
+ DEFINE_JS_FORWARDER(jsSetCodec, &Process::setCodec, "Process.setCodec")
+ DEFINE_JS_FORWARDER(jsWorkingDir, &Process::workingDirectory, "Process.workingDirectory")
+ DEFINE_JS_FORWARDER(jsSetWorkingDir, &Process::setWorkingDirectory,
+ "Process.setWorkingDirectory")
+ DEFINE_JS_FORWARDER(jsStart, &Process::start, "Process.start")
+ DEFINE_JS_FORWARDER(jsClose, &Process::close, "Process.close")
+ DEFINE_JS_FORWARDER(jsTerminate, &Process::terminate, "Process.terminate")
+ DEFINE_JS_FORWARDER(jsKill, &Process::kill, "Process.kill")
+ DEFINE_JS_FORWARDER(jsReadLine, &Process::readLine, "Process.readLine")
+ DEFINE_JS_FORWARDER(jsAtEnd, &Process::atEnd, "Process.atEnd")
+ DEFINE_JS_FORWARDER(jsReadStdOut, &Process::readStdOut, "Process.readStdOut")
+ DEFINE_JS_FORWARDER(jsReadStdErr, &Process::readStdErr, "Process.readStdErr")
+ DEFINE_JS_FORWARDER(jsCloseWriteChannel, &Process::closeWriteChannel,
+ "Process.closeWriteChannel")
+ DEFINE_JS_FORWARDER(jsWrite, &Process::write, "Process.write")
+ DEFINE_JS_FORWARDER(jsWriteLine, &Process::writeLine, "Process.writeLine")
+ DEFINE_JS_FORWARDER(jsExitCode, &Process::exitCode, "Process.exitCode")
+
+ static JSValue jsExec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ const auto args = getArguments<QString, QStringList>(ctx, "Process.exec", argc, argv);
+ bool throwOnError = false;
+ if (argc > 2)
+ throwOnError = fromArg<bool>(ctx, "Process.exec", 3, argv[2]);
+ return JS_NewInt32(ctx, fromJsObject(ctx, this_val)
+ ->exec(std::get<0>(args), std::get<1>(args), throwOnError));
+ } catch (const QString &error) { return throwError(ctx, error); }
+ }
+ static JSValue jsWaitForFinished(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ try {
+ int msecs = 30000;
+ if (argc > 0)
+ msecs = getArgument<int>(ctx, "Process.waitForFinished", argc, argv);
+ return JS_NewBool(ctx, fromJsObject(ctx, this_val)->waitForFinished(msecs));
+ } catch (const QString &error) { return throwError(ctx, error); }
+ }
+ QString getEnv(const QString &name) const { return m_environment.value(name); }
+ void setEnv(const QString &name, const QString &value) { m_environment.insert(name, value); }
+ void setCodec(const QString &codec);
- Q_INVOKABLE QString workingDirectory();
- Q_INVOKABLE void setWorkingDirectory(const QString &dir);
+ QString workingDirectory() const { return m_workingDirectory; }
+ void setWorkingDirectory(const QString &dir) { m_workingDirectory = dir; }
- Q_INVOKABLE bool start(const QString &program, const QStringList &arguments);
- Q_INVOKABLE int exec(const QString &program, const QStringList &arguments,
- bool throwOnError = false);
- Q_INVOKABLE void close();
- Q_INVOKABLE bool waitForFinished(int msecs = 30000);
- Q_INVOKABLE void terminate();
- Q_INVOKABLE void kill();
+ bool start(const QString &program, const QStringList &arguments);
+ int exec(const QString &program, const QStringList &arguments, bool throwOnError);
+ void close();
+ bool waitForFinished(int msecs);
+ void terminate() { m_qProcess->terminate(); }
+ void kill() { m_qProcess->kill(); }
- Q_INVOKABLE QString readLine();
- Q_INVOKABLE bool atEnd() const;
- Q_INVOKABLE QString readStdOut();
- Q_INVOKABLE QString readStdErr();
+ QString readLine();
+ bool atEnd() const { return m_qProcess->atEnd(); }
+ QString readStdOut() { return m_codec->toUnicode(m_qProcess->readAllStandardOutput()); }
+ QString readStdErr() { return m_codec->toUnicode(m_qProcess->readAllStandardError()); }
- Q_INVOKABLE void closeWriteChannel();
+ void closeWriteChannel() { m_qProcess->closeWriteChannel(); }
- Q_INVOKABLE void write(const QString &str);
- Q_INVOKABLE void writeLine(const QString &str);
+ void write(const QString &str) { m_qProcess->write(m_codec->fromUnicode(str)); }
+ void writeLine(const QString &str);
- Q_INVOKABLE int exitCode() const;
+ int exitCode() const { return m_qProcess->exitCode(); }
- static QScriptValue js_shellQuote(QScriptContext *context, QScriptEngine *engine);
+ static JSValue jsShellQuote(JSContext *ctx, JSValue, int argc, JSValue *argv);
private:
QString findExecutable(const QString &filePath) const;
- // ResourceAcquiringScriptObject implementation
- void releaseResources() override;
-
std::unique_ptr<QProcess> m_qProcess;
QProcessEnvironment m_environment;
QString m_workingDirectory;
QTextCodec *m_codec = nullptr;
};
-QScriptValue Process::ctor(QScriptContext *context, QScriptEngine *engine)
+JSValue Process::ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int)
{
- Process *t;
- switch (context->argumentCount()) {
- case 0:
- t = new Process(context);
- break;
- default:
- return context->throwError(QStringLiteral("Process()"));
- }
+ try {
+ JSValue obj = createObject(ctx);
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addResourceAcquiringScriptObject(t);
- const DubiousContextList dubiousContexts ({
+ Process * const process = fromJsObject(ctx, obj);
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{
DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("qbs.Process"), dubiousContexts);
-
- QScriptValue obj = engine->newQObject(t, QScriptEngine::QtOwnership);
-
- // Get environment
- QVariant v = engine->property(StringConstants::qbsProcEnvVarInternal());
- if (v.isNull()) {
- // The build environment is not initialized yet.
- // This can happen if one uses Process on the RHS of a binding like Group.name.
- t->m_environment = static_cast<ScriptEngine *>(engine)->environment();
- } else {
- t->m_environment
- = QProcessEnvironment(*reinterpret_cast<QProcessEnvironment*>(v.value<void*>()));
- }
- se->setUsesIo();
-
- return obj;
-}
-
-Process::Process(QScriptContext *context)
-{
- Q_UNUSED(context);
- Q_ASSERT(thisObject().engine() == engine());
-
- m_qProcess = std::make_unique<QProcess>();
- m_codec = QTextCodec::codecForName("UTF-8");
-}
-
-QString Process::getEnv(const QString &name)
-{
- Q_ASSERT(thisObject().engine() == engine());
- return m_environment.value(name);
+ };
+ se->checkContext(QStringLiteral("qbs.Process"), dubiousContexts);
+
+ // Get environment
+ QVariant v = se->property(StringConstants::qbsProcEnvVarInternal());
+ if (v.isNull()) {
+ // The build environment is not initialized yet.
+ // This can happen if one uses Process on the RHS of a binding like Group.name.
+ process->m_environment = se->environment();
+ } else {
+ process->m_environment
+ = QProcessEnvironment(*reinterpret_cast<QProcessEnvironment*>(v.value<void*>()));
+ }
+ se->setUsesIo();
+ return obj;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-void Process::setEnv(const QString &name, const QString &value)
+void Process::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_ASSERT(thisObject().engine() == engine());
- m_environment.insert(name, value);
+ setupMethod(ctx, classObj, "shellQuote", &Process::jsShellQuote, 3);
}
-QString Process::workingDirectory()
+void Process::setupMethods(JSContext *ctx, JSValue obj)
{
- Q_ASSERT(thisObject().engine() == engine());
- return m_workingDirectory;
+ setupMethod(ctx, obj, "getEnv", &jsGetEnv, 1);
+ setupMethod(ctx, obj, "setEnv", &jsSetEnv, 2);
+ setupMethod(ctx, obj, "setCodec", &jsSetCodec, 1);
+ setupMethod(ctx, obj, "workingDirectory", &jsWorkingDir, 0);
+ setupMethod(ctx, obj, "setWorkingDirectory", &jsSetWorkingDir, 1);
+ setupMethod(ctx, obj, "start", &jsStart, 2);
+ setupMethod(ctx, obj, "exec", &jsExec, 3);
+ setupMethod(ctx, obj, "close", &jsClose, 0);
+ setupMethod(ctx, obj, "waitForFinished", &jsWaitForFinished, 1);
+ setupMethod(ctx, obj, "terminate", &jsTerminate, 0);
+ setupMethod(ctx, obj, "kill", &jsKill, 0);
+ setupMethod(ctx, obj, "readLine", &jsReadLine, 0);
+ setupMethod(ctx, obj, "atEnd", &jsAtEnd, 0);
+ setupMethod(ctx, obj, "readStdOut", &jsReadStdOut, 0);
+ setupMethod(ctx, obj, "readStdErr", &jsReadStdErr, 0);
+ setupMethod(ctx, obj, "closeWriteChannel", &jsCloseWriteChannel, 0);
+ setupMethod(ctx, obj, "write", &jsWrite, 1);
+ setupMethod(ctx, obj, "writeLine", &jsWriteLine, 1);
+ setupMethod(ctx, obj, "exitCode", &jsExitCode, 0);
}
-void Process::setWorkingDirectory(const QString &dir)
+Process::Process(JSContext *)
{
- Q_ASSERT(thisObject().engine() == engine());
- m_workingDirectory = dir;
+ m_qProcess = std::make_unique<QProcess>();
+ m_codec = QTextCodec::codecForName("UTF-8");
}
bool Process::start(const QString &program, const QStringList &arguments)
{
- Q_ASSERT(thisObject().engine() == engine());
-
if (!m_workingDirectory.isEmpty())
m_qProcess->setWorkingDirectory(m_workingDirectory);
@@ -190,13 +219,9 @@ bool Process::start(const QString &program, const QStringList &arguments)
int Process::exec(const QString &program, const QStringList &arguments, bool throwOnError)
{
- Q_ASSERT(thisObject().engine() == engine());
-
if (!start(findExecutable(program), arguments)) {
- if (throwOnError) {
- context()->throwError(Tr::tr("Error running '%1': %2")
- .arg(program, m_qProcess->errorString()));
- }
+ if (throwOnError)
+ throw Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString());
return -1;
}
m_qProcess->closeWriteChannel();
@@ -204,8 +229,7 @@ int Process::exec(const QString &program, const QStringList &arguments, bool thr
if (throwOnError) {
if (m_qProcess->error() != QProcess::UnknownError
&& m_qProcess->error() != QProcess::Crashed) {
- context()->throwError(Tr::tr("Error running '%1': %2")
- .arg(program, m_qProcess->errorString()));
+ throw Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString());
} else if (m_qProcess->exitStatus() == QProcess::CrashExit || m_qProcess->exitCode() != 0) {
QString errorMessage = m_qProcess->error() == QProcess::Crashed
? Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString())
@@ -218,7 +242,7 @@ int Process::exec(const QString &program, const QStringList &arguments, bool thr
const QString stdErr = readStdErr();
if (!stdErr.isEmpty())
errorMessage.append(Tr::tr(" The standard error output was:\n")).append(stdErr);
- context()->throwError(errorMessage);
+ throw errorMessage;
}
}
if (m_qProcess->error() != QProcess::UnknownError)
@@ -230,32 +254,18 @@ void Process::close()
{
if (!m_qProcess)
return;
- Q_ASSERT(thisObject().engine() == engine());
m_qProcess.reset();
}
bool Process::waitForFinished(int msecs)
{
- Q_ASSERT(thisObject().engine() == engine());
-
if (m_qProcess->state() == QProcess::NotRunning)
return true;
return m_qProcess->waitForFinished(msecs);
}
-void Process::terminate()
-{
- m_qProcess->terminate();
-}
-
-void Process::kill()
-{
- m_qProcess->kill();
-}
-
void Process::setCodec(const QString &codec)
{
- Q_ASSERT(thisObject().engine() == engine());
const auto newCodec = QTextCodec::codecForName(qPrintable(codec));
if (newCodec)
m_codec = newCodec;
@@ -269,82 +279,36 @@ QString Process::readLine()
return result;
}
-bool Process::atEnd() const
-{
- return m_qProcess->atEnd();
-}
-
-QString Process::readStdOut()
-{
- return m_codec->toUnicode(m_qProcess->readAllStandardOutput());
-}
-
-QString Process::readStdErr()
-{
- return m_codec->toUnicode(m_qProcess->readAllStandardError());
-}
-
-void Process::closeWriteChannel()
-{
- m_qProcess->closeWriteChannel();
-}
-
-int Process::exitCode() const
-{
- return m_qProcess->exitCode();
-}
-
QString Process::findExecutable(const QString &filePath) const
{
ExecutableFinder exeFinder(ResolvedProductPtr(), m_environment);
return exeFinder.findExecutable(filePath, m_workingDirectory);
}
-void Process::releaseResources()
-{
- close();
- deleteLater();
-}
-
-void Process::write(const QString &str)
-{
- m_qProcess->write(m_codec->fromUnicode(str));
-}
-
void Process::writeLine(const QString &str)
{
m_qProcess->write(m_codec->fromUnicode(str));
m_qProcess->putChar('\n');
}
-QScriptValue Process::js_shellQuote(QScriptContext *context, QScriptEngine *engine)
+JSValue Process::jsShellQuote(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("shellQuote expects at least 2 arguments"));
- }
- const QString program = context->argument(0).toString();
- const QStringList args = context->argument(1).toVariant().toStringList();
- HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
- if (context->argumentCount() > 2) {
- hostOs = context->argument(2).toVariant().toStringList().contains(QLatin1String("windows"))
- ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
- }
- return engine->toScriptValue(shellQuote(program, args, hostOs));
+ try {
+ const auto args = getArguments<QString, QStringList>(ctx, "Process.shellQuote", argc, argv);
+ HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
+ if (argc > 2) {
+ const auto osList = fromArg<QStringList>(ctx, "Process.shellQuote", 3, argv[2]);
+ hostOs = osList.contains(QLatin1String("windows"))
+ ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
+ }
+ return makeJsString(ctx, shellQuote(std::get<0>(args), std::get<1>(args), hostOs));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionProcess(QScriptValue extensionObject)
+void initializeJsExtensionProcess(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(&Process::staticMetaObject, engine->newFunction(&Process::ctor));
- extensionObject.setProperty(QStringLiteral("Process"), obj);
- obj.setProperty(QStringLiteral("shellQuote"), engine->newFunction(Process::js_shellQuote, 3));
+ qbs::Internal::Process::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::Process *)
-
-#include "process.moc"
diff --git a/src/lib/corelib/jsextensions/propertylist.cpp b/src/lib/corelib/jsextensions/propertylist.cpp
index 197d5e99e..914701203 100644
--- a/src/lib/corelib/jsextensions/propertylist.cpp
+++ b/src/lib/corelib/jsextensions/propertylist.cpp
@@ -38,11 +38,11 @@
**
****************************************************************************/
-#include <QtScript/qscriptengine.h>
+#include <language/scriptengine.h>
-void initializeJsExtensionPropertyList(QScriptValue extensionObject)
+void initializeJsExtensionPropertyList(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newObject(); // provide a fake object
- extensionObject.setProperty(QStringLiteral("PropertyList"), obj);
+ JSValue obj = engine->newObject(); // provide a fake object
+ qbs::Internal::setJsProperty(engine->context(), extensionObject,
+ QStringLiteral("PropertyList"), obj);
}
diff --git a/src/lib/corelib/jsextensions/propertylist_darwin.h b/src/lib/corelib/jsextensions/propertylist_darwin.h
deleted file mode 100644
index 279309681..000000000
--- a/src/lib/corelib/jsextensions/propertylist_darwin.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2015 Petroules Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QBS_PROPERTYLIST_H
-#define QBS_PROPERTYLIST_H
-
-#include <QtCore/qglobal.h>
-
-#include <QtCore/qobject.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qvariant.h>
-
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptvalue.h>
-
-#include <memory>
-
-namespace qbs {
-namespace Internal {
-
-void initializeJsExtensionPropertyList(QScriptValue extensionObject);
-
-class PropertyListPrivate;
-
-// We need to have this class in the header since CMake's automoc doesn't handle .mm files
-class PropertyList : public QObject, public QScriptable
-{
- Q_OBJECT
-public:
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
- PropertyList(QScriptContext *context);
- ~PropertyList() override;
- Q_INVOKABLE bool isEmpty() const;
- Q_INVOKABLE void clear();
- Q_INVOKABLE void readFromObject(const QScriptValue &value);
- Q_INVOKABLE void readFromString(const QString &input);
- Q_INVOKABLE void readFromFile(const QString &filePath);
- Q_INVOKABLE void readFromData(const QByteArray &data);
- Q_INVOKABLE void writeToFile(const QString &filePath, const QString &plistFormat);
- Q_INVOKABLE QScriptValue format() const;
- Q_INVOKABLE QScriptValue toObject() const;
- Q_INVOKABLE QString toString(const QString &plistFormat) const;
- Q_INVOKABLE QString toXMLString() const;
- Q_INVOKABLE QString toJSON(const QString &style = QString()) const;
-private:
- const std::unique_ptr<PropertyListPrivate> d;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-Q_DECLARE_METATYPE(qbs::Internal::PropertyList *)
-
-#endif // QBS_PROPERTYLIST_H
diff --git a/src/lib/corelib/jsextensions/propertylist_darwin.mm b/src/lib/corelib/jsextensions/propertylist_darwin.mm
index caf9feb3e..69aeefd92 100644
--- a/src/lib/corelib/jsextensions/propertylist_darwin.mm
+++ b/src/lib/corelib/jsextensions/propertylist_darwin.mm
@@ -38,92 +38,120 @@
**
****************************************************************************/
-#include "propertylist_darwin.h"
+#include "jsextension.h"
+#include "propertylistutils.h"
#include <language/scriptengine.h>
+#include <logging/translator.h>
#include <tools/hostosinfo.h>
#include <QtCore/qfile.h>
-#include <QtCore/qobject.h>
#include <QtCore/qstring.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-
-// Same values as CoreFoundation and Foundation APIs
-enum {
- QPropertyListOpenStepFormat = 1,
- QPropertyListXMLFormat_v1_0 = 100,
- QPropertyListBinaryFormat_v1_0 = 200,
- QPropertyListJSONFormat = 1000 // If this conflicts someday, just change it :)
-};
-
namespace qbs {
namespace Internal {
-class PropertyListPrivate
+class PropertyList : public JsExtension<PropertyList>
{
public:
- PropertyListPrivate();
+ static const char *name() { return "PropertyList"; }
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int);
+ PropertyList(JSContext *) {}
+ static void setupMethods(JSContext *ctx, JSValue obj);
+
+ DEFINE_JS_FORWARDER(jsIsEmpty, &PropertyList::isEmpty, "PropertyList.isEmpty");
+ DEFINE_JS_FORWARDER(jsClear, &PropertyList::clear, "PropertyList.clear");
+ DEFINE_JS_FORWARDER(jsReadFromObject, &PropertyList::readFromObject,
+ "PropertyList.readFromObject");
+ DEFINE_JS_FORWARDER(jsReadFromString, &PropertyList::readFromString,
+ "PropertyList.readFromString");
+ DEFINE_JS_FORWARDER(jsReadFromFile, &PropertyList::readFromFile, "PropertyList.readFromFile");
+ DEFINE_JS_FORWARDER(jsReadFromData, &PropertyList::readFromData, "PropertyList.readFromData");
+ DEFINE_JS_FORWARDER(jsWriteToFile, &PropertyList::writeToFile, "PropertyList.writeToFile");
+ DEFINE_JS_FORWARDER(jsFormat, &PropertyList::format, "PropertyList.format");
+ DEFINE_JS_FORWARDER(jsToObject, &PropertyList::toObject, "PropertyList.toObject");
+ DEFINE_JS_FORWARDER(jsToString, &PropertyList::toString, "PropertyList.toString");
+ DEFINE_JS_FORWARDER(jsToXmlString, &PropertyList::toXMLString, "PropertyList.toXMLString");
+
+ static JSValue jsToJson(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ QString style;
+ if (argc > 0)
+ style = fromArg<QString>(ctx, "Process.exec", 1, argv[0]);
+ return toJsValue(ctx, fromJsObject(ctx, this_val)->toJSON(style));
+ } catch (const QString &error) { return throwError(ctx, error); }
+ }
- QVariant propertyListObject;
- int propertyListFormat;
+private:
+ bool isEmpty() const { return m_propertyListObject.isNull(); }
+ void clear();
+ void readFromObject(const QVariant &value);
+ void readFromString(const QString &input);
+ void readFromFile(const QString &filePath);
+ void readFromData(const QByteArray &data);
+ void writeToFile(const QString &filePath, const QString &plistFormat);
+ std::optional<QString> format() const;
+ QVariant toObject() const { return m_propertyListObject; }
+ QString toString(const QString &plistFormat) const;
+ QString toXMLString() const;
+ QString toJSON(const QString &style = QString()) const;
+
+ QByteArray writeToData(const QString &format) const;
+
+ QVariant m_propertyListObject;
+ int m_propertyListFormat = 0;
+};
- void readFromData(QScriptContext *context, const QByteArray &data);
- QByteArray writeToData(QScriptContext *context, const QString &format);
+// Same values as CoreFoundation and Foundation APIs
+enum {
+ QPropertyListOpenStepFormat = 1,
+ QPropertyListXMLFormat_v1_0 = 100,
+ QPropertyListBinaryFormat_v1_0 = 200,
+ QPropertyListJSONFormat = 1000 // If this conflicts someday, just change it :)
};
-QScriptValue PropertyList::ctor(QScriptContext *context, QScriptEngine *engine)
+JSValue PropertyList::ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int)
{
- auto const se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({
+ try {
+ JSValue obj = createObject(ctx);
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{
DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("qbs.PropertyList"), dubiousContexts);
-
- auto p = new PropertyList(context);
- QScriptValue obj = engine->newQObject(p, QScriptEngine::ScriptOwnership);
- return obj;
-}
-
-PropertyListPrivate::PropertyListPrivate()
- : propertyListObject(), propertyListFormat(0)
-{
-}
-
-PropertyList::~PropertyList() = default;
-
-PropertyList::PropertyList(QScriptContext *context)
- : d(std::make_unique<PropertyListPrivate>())
-{
- Q_UNUSED(context);
- Q_ASSERT(thisObject().engine() == engine());
+ };
+ se->checkContext(QStringLiteral("qbs.PropertyList"), dubiousContexts);
+ return obj;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-bool PropertyList::isEmpty() const
+void PropertyList::setupMethods(JSContext *ctx, JSValue obj)
{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- return p->d->propertyListObject.isNull();
+ setupMethod(ctx, obj, "isEmpty", &jsIsEmpty, 0);
+ setupMethod(ctx, obj, "clear", &jsClear, 0);
+ setupMethod(ctx, obj, "readFromObject", &jsReadFromObject, 1);
+ setupMethod(ctx, obj, "readFromString", &jsReadFromString, 1);
+ setupMethod(ctx, obj, "readFromFile", &jsReadFromFile, 1);
+ setupMethod(ctx, obj, "readFromData", &jsReadFromData, 1);
+ setupMethod(ctx, obj, "writeToFile", &jsWriteToFile, 1);
+ setupMethod(ctx, obj, "format", &jsFormat, 0);
+ setupMethod(ctx, obj, "toObject", &jsToObject, 0);
+ setupMethod(ctx, obj, "toString", &jsToString, 1);
+ setupMethod(ctx, obj, "toXMLString", &jsToXmlString, 1);
+ setupMethod(ctx, obj, "toJSON", &jsToJson, 1);
}
void PropertyList::clear()
{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- p->d->propertyListObject = QVariant();
- p->d->propertyListFormat = 0;
+ m_propertyListObject = QVariant();
+ m_propertyListFormat = 0;
}
-void PropertyList::readFromObject(const QScriptValue &value)
+void PropertyList::readFromObject(const QVariant &value)
{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- p->d->propertyListObject = value.toVariant();
- p->d->propertyListFormat = 0; // wasn't deserialized from any external format
+ m_propertyListObject = value;
+ m_propertyListFormat = 0; // wasn't deserialized from any external format
}
void PropertyList::readFromString(const QString &input)
@@ -133,49 +161,68 @@ void PropertyList::readFromString(const QString &input)
void PropertyList::readFromFile(const QString &filePath)
{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
-
QFile file(filePath);
if (file.open(QIODevice::ReadOnly)) {
const QByteArray data = file.readAll();
if (file.error() == QFile::NoError) {
- p->d->readFromData(p->context(), data);
+ readFromData(data);
return;
}
}
-
- p->context()->throwError(QStringLiteral("%1: %2").arg(filePath).arg(file.errorString()));
+ throw QStringLiteral("%1: %2").arg(filePath).arg(file.errorString());
}
void PropertyList::readFromData(const QByteArray &data)
{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- p->d->readFromData(p->context(), data);
+ @autoreleasepool {
+ NSPropertyListFormat format;
+ int internalFormat = 0;
+ NSString *errorString = nil;
+ id plist = [NSPropertyListSerialization propertyListWithData:data.toNSData()
+ options:0
+ format:&format error:nil];
+ if (plist) {
+ internalFormat = format;
+ } else {
+ NSError *error = nil;
+ plist = [NSJSONSerialization JSONObjectWithData:data.toNSData()
+ options:0
+ error:&error];
+ if (Q_UNLIKELY(!plist)) {
+ errorString = [error localizedDescription];
+ } else {
+ internalFormat = QPropertyListJSONFormat;
+ }
+ }
+
+ if (Q_UNLIKELY(!plist))
+ throw QString::fromNSString(errorString);
+ QVariant obj = QPropertyListUtils::fromPropertyList(plist);
+ if (!obj.isNull()) {
+ m_propertyListObject = obj;
+ m_propertyListFormat = internalFormat;
+ } else {
+ throw Tr::tr("error converting property list");
+ }
+ }
}
void PropertyList::writeToFile(const QString &filePath, const QString &plistFormat)
{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
-
QFile file(filePath);
- QByteArray data = p->d->writeToData(p->context(), plistFormat);
+ QByteArray data = writeToData(plistFormat);
if (Q_LIKELY(!data.isEmpty())) {
if (file.open(QIODevice::WriteOnly) && file.write(data) == data.size()) {
return;
}
}
- p->context()->throwError(QStringLiteral("%1: %2").arg(filePath).arg(file.errorString()));
+ throw QStringLiteral("%1: %2").arg(filePath).arg(file.errorString());
}
-QScriptValue PropertyList::format() const
+std::optional<QString> PropertyList::format() const
{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- switch (p->d->propertyListFormat)
+ switch (m_propertyListFormat)
{
case QPropertyListOpenStepFormat:
return QStringLiteral("openstep");
@@ -186,32 +233,20 @@ QScriptValue PropertyList::format() const
case QPropertyListJSONFormat:
return QStringLiteral("json");
default:
- return p->engine()->undefinedValue();
+ return {};
}
}
-QScriptValue PropertyList::toObject() const
-{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- return p->engine()->toScriptValue(p->d->propertyListObject);
-}
-
QString PropertyList::toString(const QString &plistFormat) const
{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
-
if (plistFormat == QLatin1String("binary1")) {
- p->context()->throwError(QStringLiteral("Property list object cannot be converted to a "
- "string in the binary1 format; this format can only "
- "be written directly to a file"));
- return {};
+ throw Tr::tr("Property list object cannot be converted to a "
+ "string in the binary1 format; this format can only "
+ "be written directly to a file");
}
if (!isEmpty())
- return QString::fromUtf8(p->d->writeToData(p->context(), plistFormat));
-
+ return QString::fromUtf8(writeToData(plistFormat));
return {};
}
@@ -229,63 +264,16 @@ QString PropertyList::toJSON(const QString &style) const
return toString(format);
}
-} // namespace Internal
-} // namespace qbs
-
-#include "propertylistutils.h"
-
-namespace qbs {
-namespace Internal {
-
-void PropertyListPrivate::readFromData(QScriptContext *context, const QByteArray &data)
-{
- @autoreleasepool {
- NSPropertyListFormat format;
- int internalFormat = 0;
- NSString *errorString = nil;
- id plist = [NSPropertyListSerialization propertyListWithData:data.toNSData()
- options:0
- format:&format error:nil];
- if (plist) {
- internalFormat = format;
- } else {
- NSError *error = nil;
- plist = [NSJSONSerialization JSONObjectWithData:data.toNSData()
- options:0
- error:&error];
- if (Q_UNLIKELY(!plist)) {
- errorString = [error localizedDescription];
- } else {
- internalFormat = QPropertyListJSONFormat;
- }
- }
-
- if (Q_UNLIKELY(!plist)) {
- context->throwError(QString::fromNSString(errorString));
- } else {
- QVariant obj = QPropertyListUtils::fromPropertyList(plist);
- if (!obj.isNull()) {
- propertyListObject = obj;
- propertyListFormat = internalFormat;
- } else {
- context->throwError(QStringLiteral("error converting property list"));
- }
- }
- }
-}
-
-QByteArray PropertyListPrivate::writeToData(QScriptContext *context, const QString &format)
+QByteArray PropertyList::writeToData(const QString &format) const
{
@autoreleasepool {
NSError *error = nil;
NSString *errorString = nil;
NSData *data = nil;
- id obj = QPropertyListUtils::toPropertyList(propertyListObject);
- if (!obj) {
- context->throwError(QStringLiteral("error converting property list"));
- return QByteArray();
- }
+ id obj = QPropertyListUtils::toPropertyList(m_propertyListObject);
+ if (!obj)
+ throw Tr::tr("error converting property list");
if (format == QLatin1String("json") || format == QLatin1String("json-pretty") ||
format == QLatin1String("json-compact")) {
@@ -322,9 +310,8 @@ QByteArray PropertyListPrivate::writeToData(QScriptContext *context, const QStri
@"format", format.toUtf8().constData()];
}
- if (Q_UNLIKELY(!data)) {
- context->throwError(QString::fromNSString(errorString));
- }
+ if (Q_UNLIKELY(!data))
+ throw QString::fromNSString(errorString);
return QByteArray::fromNSData(data);
}
@@ -333,11 +320,7 @@ QByteArray PropertyListPrivate::writeToData(QScriptContext *context, const QStri
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionPropertyList(QScriptValue extensionObject)
+void initializeJsExtensionPropertyList(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(&PropertyList::staticMetaObject,
- engine->newFunction(&PropertyList::ctor));
- extensionObject.setProperty(QStringLiteral("PropertyList"), obj);
+ qbs::Internal::PropertyList::registerClass(engine, extensionObject);
}
diff --git a/src/lib/corelib/jsextensions/temporarydir.cpp b/src/lib/corelib/jsextensions/temporarydir.cpp
index 470d21d20..209c058f2 100644
--- a/src/lib/corelib/jsextensions/temporarydir.cpp
+++ b/src/lib/corelib/jsextensions/temporarydir.cpp
@@ -38,17 +38,14 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qobject.h>
#include <QtCore/qtemporarydir.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
@@ -60,63 +57,49 @@ static bool tempDirIsCanonical()
return false;
}
-class TemporaryDir : public QObject, public QScriptable
+class TemporaryDir : public JsExtension<TemporaryDir>
{
- Q_OBJECT
+ friend class JsExtension<TemporaryDir>;
public:
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
- TemporaryDir(QScriptContext *context);
- Q_INVOKABLE bool isValid() const;
- Q_INVOKABLE QString path() const;
- Q_INVOKABLE bool remove();
+ static const char *name() { return "TemporaryDir"; }
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int)
+ {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{DubiousContext(EvalContext::PropertyEvaluation,
+ DubiousContext::SuggestMoving)};
+ se->checkContext(QStringLiteral("qbs.TemporaryDir"), dubiousContexts);
+ return createObject(ctx);
+ }
+ static void setupMethods(JSContext *ctx, JSValue obj)
+ {
+ setupMethod(ctx, obj, "isValid", &jsIsValid, 0);
+ setupMethod(ctx, obj, "path", &jsPath, 0);
+ setupMethod(ctx, obj, "remove", &jsRemove, 0);
+ }
+
private:
- QTemporaryDir dir;
-};
+ TemporaryDir(JSContext *) { m_dir.setAutoRemove(false); }
-QScriptValue TemporaryDir::ctor(QScriptContext *context, QScriptEngine *engine)
-{
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({
- DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("qbs.TemporaryDir"), dubiousContexts);
+ DEFINE_JS_FORWARDER(jsIsValid, &TemporaryDir::isValid, "TemporaryDir.isValid")
+ DEFINE_JS_FORWARDER(jsPath, &TemporaryDir::path, "TemporaryDir.path")
+ DEFINE_JS_FORWARDER(jsRemove, &TemporaryDir::remove, "TemporaryDir.remove")
- const auto t = new TemporaryDir(context);
- QScriptValue obj = engine->newQObject(t, QScriptEngine::ScriptOwnership);
- return obj;
-}
+ bool isValid() const { return m_dir.isValid(); }
-TemporaryDir::TemporaryDir(QScriptContext *context)
-{
- Q_UNUSED(context);
- dir.setAutoRemove(false);
-}
+ QString path() const
+ {
+ return tempDirIsCanonical() ? m_dir.path() : QFileInfo(m_dir.path()).canonicalFilePath();
+ }
-bool TemporaryDir::isValid() const
-{
- return dir.isValid();
-}
-
-QString TemporaryDir::path() const
-{
- return tempDirIsCanonical() ? dir.path() : QFileInfo(dir.path()).canonicalFilePath();
-}
+ bool remove() { return m_dir.remove(); }
-bool TemporaryDir::remove()
-{
- return dir.remove();
-}
+ QTemporaryDir m_dir;
+};
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionTemporaryDir(QScriptValue extensionObject)
+void initializeJsExtensionTemporaryDir(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(&TemporaryDir::staticMetaObject,
- engine->newFunction(&TemporaryDir::ctor));
- extensionObject.setProperty(QStringLiteral("TemporaryDir"), obj);
+ qbs::Internal::TemporaryDir::registerClass(engine, extensionObject);
}
-
-#include "temporarydir.moc"
diff --git a/src/lib/corelib/jsextensions/textfile.cpp b/src/lib/corelib/jsextensions/textfile.cpp
index 04d764377..c9f0edec9 100644
--- a/src/lib/corelib/jsextensions/textfile.cpp
+++ b/src/lib/corelib/jsextensions/textfile.cpp
@@ -37,13 +37,14 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/hostosinfo.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qobject.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qvariant.h>
@@ -53,16 +54,12 @@
#include <QtCore/qtextcodec.h>
#endif
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
-class TextFile : public QObject, public QScriptable, public ResourceAcquiringScriptObject
+class TextFile : public JsExtension<TextFile>
{
- Q_OBJECT
+ friend class JsExtension<TextFile>;
public:
enum OpenMode
{
@@ -71,77 +68,92 @@ public:
ReadWrite = ReadOnly | WriteOnly,
Append = 4
};
- Q_ENUM(OpenMode)
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
-
- Q_INVOKABLE void close();
- Q_INVOKABLE QString filePath();
- Q_INVOKABLE void setCodec(const QString &codec);
- Q_INVOKABLE QString readLine();
- Q_INVOKABLE QString readAll();
- Q_INVOKABLE bool atEof() const;
- Q_INVOKABLE void truncate();
- Q_INVOKABLE void write(const QString &str);
- Q_INVOKABLE void writeLine(const QString &str);
+ static const char *name() { return "TextFile"; }
+ static void declareEnums(JSContext *ctx, JSValue classObj);
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int);
+ static void setupMethods(JSContext *ctx, JSValue obj);
private:
- TextFile(QScriptContext *context, const QString &filePath, OpenMode mode = ReadOnly,
- const QString &codec = QLatin1String("UTF-8"));
-
- bool checkForClosed() const;
-
- // ResourceAcquiringScriptObject implementation
- void releaseResources() override;
+ DEFINE_JS_FORWARDER(jsClose, &TextFile::close, "TextFile.close")
+ DEFINE_JS_FORWARDER(jsFilePath, &TextFile::filePath, "TextFile.filePath")
+ DEFINE_JS_FORWARDER(jsSetCodec, &TextFile::setCodec, "TextFile.setCodec")
+ DEFINE_JS_FORWARDER(jsReadLine, &TextFile::readLine, "TextFile.readLine")
+ DEFINE_JS_FORWARDER(jsReadAll, &TextFile::readAll, "TextFile.readAll")
+ DEFINE_JS_FORWARDER(jsAtEof, &TextFile::atEof, "TextFile.atEof")
+ DEFINE_JS_FORWARDER(jsTruncate, &TextFile::truncate, "TextFile.truncate")
+ DEFINE_JS_FORWARDER(jsWrite, &TextFile::write, "TextFile.write")
+ DEFINE_JS_FORWARDER(jsWriteLine, &TextFile::writeLine, "TextFile.writeLine")
+
+ void close();
+ QString filePath();
+ void setCodec(const QString &codec);
+ QString readLine();
+ QString readAll();
+ bool atEof() const;
+ void truncate();
+ void write(const QString &str);
+ void writeLine(const QString &str);
+
+ TextFile(JSContext *, const QString &filePath, OpenMode mode, const QString &codec);
+
+ void checkForClosed() const;
std::unique_ptr<QFile> m_file;
QTextCodec *m_codec = nullptr;
};
-QScriptValue TextFile::ctor(QScriptContext *context, QScriptEngine *engine)
+void TextFile::declareEnums(JSContext *ctx, JSValue classObj)
{
- TextFile *t;
- switch (context->argumentCount()) {
- case 0:
- return context->throwError(Tr::tr("TextFile constructor needs path of file to be opened."));
- case 1:
- t = new TextFile(context, context->argument(0).toString());
- break;
- case 2:
- t = new TextFile(context,
- context->argument(0).toString(),
- static_cast<OpenMode>(context->argument(1).toInt32())
- );
- break;
- case 3:
- t = new TextFile(context,
- context->argument(0).toString(),
- static_cast<OpenMode>(context->argument(1).toInt32()),
- context->argument(2).toString()
- );
- break;
- default:
- return context->throwError(Tr::tr("TextFile constructor takes at most three parameters."));
- }
+ DECLARE_ENUM(ctx, classObj, ReadOnly);
+ DECLARE_ENUM(ctx, classObj, WriteOnly);
+ DECLARE_ENUM(ctx, classObj, ReadWrite);
+ DECLARE_ENUM(ctx, classObj, Append);
+}
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addResourceAcquiringScriptObject(t);
- const DubiousContextList dubiousContexts({
+JSValue TextFile::ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int)
+{
+ try {
+ const auto filePath = getArgument<QString>(ctx, "TextFile constructor", argc, argv);
+ OpenMode mode = ReadOnly;
+ QString codec = QLatin1String("UTF-8");
+ if (argc > 1) {
+ mode = static_cast<OpenMode>
+ (fromArg<qint32>(ctx, "TextFile constructor", 2, argv[1]));
+ }
+ if (argc > 2) {
+ codec = fromArg<QString>(ctx, "TextFile constructor", 3, argv[2]);
+ }
+ ScopedJsValue obj(ctx, createObject(ctx, filePath, mode, codec));
+
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts {
DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("qbs.TextFile"), dubiousContexts);
- se->setUsesIo();
-
- return engine->newQObject(t, QScriptEngine::QtOwnership);
+ };
+ se->checkContext(QStringLiteral("qbs.TextFile"), dubiousContexts);
+ se->setUsesIo();
+ return obj.release();
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-TextFile::TextFile(QScriptContext *context, const QString &filePath, OpenMode mode,
- const QString &codec)
+void TextFile::setupMethods(JSContext *ctx, JSValue obj)
{
- Q_UNUSED(codec)
- Q_ASSERT(thisObject().engine() == engine());
+ setupMethod(ctx, obj, "close", &jsClose, 0);
+ setupMethod(ctx, obj, "filePath", &jsFilePath, 0);
+ setupMethod(ctx, obj, "atEof", &jsAtEof, 0);
+ setupMethod(ctx, obj, "setCodec", &jsSetCodec, 1);
+ setupMethod(ctx, obj, "readLine", &jsReadLine, 0);
+ setupMethod(ctx, obj, "readAll", &jsReadAll, 0);
+ setupMethod(ctx, obj, "truncate", &jsTruncate, 0);
+ setupMethod(ctx, obj, "write", &jsWrite, 1);
+ setupMethod(ctx, obj, "writeLine", &jsWriteLine, 1);
+}
- m_file = std::make_unique<QFile>(filePath);
+TextFile::TextFile(JSContext *, const QString &filePath, OpenMode mode, const QString &codec)
+{
+ auto file = std::make_unique<QFile>(filePath);
const auto newCodec = QTextCodec::codecForName(qPrintable(codec));
m_codec = newCodec ? newCodec : QTextCodec::codecForName("UTF-8");
QIODevice::OpenMode m = QIODevice::NotOpen;
@@ -152,32 +164,27 @@ TextFile::TextFile(QScriptContext *context, const QString &filePath, OpenMode mo
if (mode & Append)
m |= QIODevice::Append;
m |= QIODevice::Text;
- if (Q_UNLIKELY(!m_file->open(m))) {
- context->throwError(Tr::tr("Unable to open file '%1': %2")
- .arg(filePath, m_file->errorString()));
- m_file.reset();
- }
+ if (Q_UNLIKELY(!file->open(m)))
+ throw Tr::tr("Unable to open file '%1': %2").arg(filePath, file->errorString());
+ m_file = std::move(file);
}
void TextFile::close()
{
- if (checkForClosed())
- return;
+ checkForClosed();
m_file->close();
m_file.reset();
}
QString TextFile::filePath()
{
- if (checkForClosed())
- return {};
+ checkForClosed();
return QFileInfo(*m_file).absoluteFilePath();
}
void TextFile::setCodec(const QString &codec)
{
- if (checkForClosed())
- return;
+ checkForClosed();
const auto newCodec = QTextCodec::codecForName(qPrintable(codec));
if (newCodec)
m_codec = newCodec;
@@ -185,8 +192,7 @@ void TextFile::setCodec(const QString &codec)
QString TextFile::readLine()
{
- if (checkForClosed())
- return {};
+ checkForClosed();
auto result = m_codec->toUnicode(m_file->readLine());
if (!result.isEmpty() && result.back() == QLatin1Char('\n'))
result.chop(1);
@@ -195,68 +201,45 @@ QString TextFile::readLine()
QString TextFile::readAll()
{
- if (checkForClosed())
- return {};
+ checkForClosed();
return m_codec->toUnicode(m_file->readAll());
}
bool TextFile::atEof() const
{
- if (checkForClosed())
- return true;
+ checkForClosed();
return m_file->atEnd();
}
void TextFile::truncate()
{
- if (checkForClosed())
- return;
+ checkForClosed();
m_file->resize(0);
}
void TextFile::write(const QString &str)
{
- if (checkForClosed())
- return;
+ checkForClosed();
m_file->write(m_codec->fromUnicode(str));
}
void TextFile::writeLine(const QString &str)
{
- if (checkForClosed())
- return;
+ checkForClosed();
m_file->write(m_codec->fromUnicode(str));
m_file->putChar('\n');
}
-bool TextFile::checkForClosed() const
+void TextFile::checkForClosed() const
{
- if (m_file)
- return false;
- QScriptContext *ctx = context();
- if (ctx)
- ctx->throwError(Tr::tr("Access to TextFile object that was already closed."));
- return true;
-}
-
-void TextFile::releaseResources()
-{
- close();
- deleteLater();
+ if (!m_file)
+ throw Tr::tr("Access to TextFile object that was already closed.");
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionTextFile(QScriptValue extensionObject)
+void initializeJsExtensionTextFile(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(&TextFile::staticMetaObject,
- engine->newFunction(&TextFile::ctor));
- extensionObject.setProperty(QStringLiteral("TextFile"), obj);
+ qbs::Internal::TextFile::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::TextFile *)
-
-#include "textfile.moc"
diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp
index 5426658a9..ea8eb0ddc 100644
--- a/src/lib/corelib/jsextensions/utilitiesextension.cpp
+++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp
@@ -37,8 +37,10 @@
**
****************************************************************************/
+#include "jsextension.h"
+#include "jsextensions.h"
+
#include <api/languageinfo.h>
-#include <jsextensions/jsextensions.h>
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/architectures.h>
@@ -84,9 +86,6 @@ struct fat_arch_64 {
#include <QtCore/qfile.h>
#include <QtCore/qlibrary.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
@@ -94,112 +93,141 @@ class DummyLogSink : public ILogSink {
void doPrintMessage(LoggerLevel, const QString &, const QString &) override { }
};
-class UtilitiesExtension : public QObject, QScriptable
+class UtilitiesExtension : public JsExtension<UtilitiesExtension>
{
- Q_OBJECT
public:
- static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalPlatform(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalTargetArchitecture(QScriptContext *context,
- QScriptEngine *engine);
- static QScriptValue js_canonicalToolchain(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_cStringQuote(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_getHash(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_getNativeSetting(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_kernelVersion(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_nativeSettingGroups(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_rfc1034identifier(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue js_smimeMessageContent(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_certificateInfo(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_signingIdentities(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_clangClCompilerInfo(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_installedMSVCs(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_installedClangCls(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue js_versionCompare(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue js_qmlTypeInfo(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_builtinExtensionNames(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_isSharedLibrary(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue js_getArchitecturesFromBinary(QScriptContext *context,
- QScriptEngine *engine);
+ static const char *name() { return "Utilities"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static JSValue js_canonicalArchitecture(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_canonicalPlatform(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_canonicalTargetArchitecture(JSContext *ctx, JSValueConst,
+ int, JSValueConst *);
+ static JSValue js_canonicalToolchain(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_cStringQuote(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_getHash(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_getNativeSetting(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_kernelVersion(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_nativeSettingGroups(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_rfc1034identifier(JSContext *ctx, JSValueConst, int, JSValueConst *);
+
+ static JSValue js_smimeMessageContent(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_certificateInfo(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_signingIdentities(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_msvcCompilerInfo(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_clangClCompilerInfo(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_installedMSVCs(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_installedClangCls(JSContext *ctx, JSValueConst, int, JSValueConst *);
+
+ static JSValue js_versionCompare(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+
+ static JSValue js_qmlTypeInfo(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_builtinExtensionNames(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_isSharedLibrary(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+
+ static JSValue js_getArchitecturesFromBinary(JSContext *ctx, JSValueConst, int, JSValueConst *);
};
-QScriptValue UtilitiesExtension::js_ctor(QScriptContext *context, QScriptEngine *engine)
-{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'Utilities' cannot be instantiated."));
-}
-
-QScriptValue UtilitiesExtension::js_canonicalPlatform(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_canonicalPlatform(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- const QScriptValue value = context->argument(0);
- if (value.isUndefined() || value.isNull())
- return engine->toScriptValue(QStringList());
-
- if (context->argumentCount() == 1 && value.isString()) {
- return engine->toScriptValue([&value] {
- QStringList list;
- for (const auto &s : HostOsInfo::canonicalOSIdentifiers(value.toString()))
- list.push_back(s);
- return list;
- }());
+ try {
+ const auto platform = getArgument<QString>(ctx, "Utilities.canonicalPlatform", argc, argv);
+ if (platform.isEmpty())
+ return makeJsStringList(ctx, {});
+ std::vector<QString> platforms = HostOsInfo::canonicalOSIdentifiers(platform);
+ return makeJsStringList(ctx, QStringList(std::move_iterator(platforms.begin()),
+ std::move_iterator(platforms.end())));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("canonicalPlatform expects one argument of type string"));
}
-QScriptValue UtilitiesExtension::js_canonicalTargetArchitecture(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_canonicalTargetArchitecture(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- const QScriptValue arch = context->argument(0);
- if (arch.isUndefined() || arch.isNull())
- return arch;
-
- QScriptValue endianness = context->argument(1);
- if (endianness.isUndefined() || endianness.isNull())
- endianness = QString();
- const QScriptValue vendor = context->argument(2);
- const QScriptValue system = context->argument(3);
- const QScriptValue abi = context->argument(4);
-
- if (!arch.isString() || !endianness.isString()
- || !vendor.isString() || !system.isString() || !abi.isString())
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("canonicalTargetArchitecture expects 1 to 5 arguments of type string"));
-
- return engine->toScriptValue(canonicalTargetArchitecture(arch.toString(), endianness.toString(),
- vendor.toString(),
- system.toString(), abi.toString()));
+ try {
+ const auto arch = getArgument<QString>(ctx, "Utilities.canonicalTargetArchitecture",
+ argc, argv);
+ QString endianness, vendor, system, abi;
+ if (argc > 1)
+ endianness = fromArg<QString>(ctx, "Utilities.canonicalTargetArchitecture", 2, argv[1]);
+ if (argc > 2)
+ vendor = fromArg<QString>(ctx, "Utilities.canonicalTargetArchitecture", 3, argv[2]);
+ if (argc > 3)
+ system = fromArg<QString>(ctx, "Utilities.canonicalTargetArchitecture", 4, argv[3]);
+ if (argc > 4)
+ abi = fromArg<QString>(ctx, "Utilities.canonicalTargetArchitecture", 5, argv[4]);
+ return makeJsString(ctx,
+ canonicalTargetArchitecture(arch, endianness, vendor, system, abi));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue UtilitiesExtension::js_canonicalArchitecture(QScriptContext *context,
- QScriptEngine *engine)
+void UtilitiesExtension::setupStaticMethods(JSContext *ctx, JSValue classObj)
+{
+ setupMethod(ctx, classObj, "canonicalArchitecture",
+ &UtilitiesExtension::js_canonicalArchitecture, 1);
+ setupMethod(ctx, classObj, "canonicalPlatform",
+ &UtilitiesExtension::js_canonicalPlatform, 1);
+ setupMethod(ctx, classObj, "canonicalTargetArchitecture",
+ &UtilitiesExtension::js_canonicalTargetArchitecture, 1);
+ setupMethod(ctx, classObj, "canonicalToolchain",
+ &UtilitiesExtension::js_canonicalToolchain, 1);
+ setupMethod(ctx, classObj, "cStringQuote", &UtilitiesExtension::js_cStringQuote, 1);
+ setupMethod(ctx, classObj, "getHash", &UtilitiesExtension::js_getHash, 1);
+ setupMethod(ctx, classObj, "getNativeSetting",
+ &UtilitiesExtension::js_getNativeSetting, 3);
+ setupMethod(ctx, classObj, "kernelVersion", &UtilitiesExtension::js_kernelVersion, 0);
+ setupMethod(ctx, classObj, "nativeSettingGroups",
+ &UtilitiesExtension::js_nativeSettingGroups, 1);
+ setupMethod(ctx, classObj, "rfc1034Identifier",
+ &UtilitiesExtension::js_rfc1034identifier, 1);
+ setupMethod(ctx, classObj, "smimeMessageContent",
+ &UtilitiesExtension::js_smimeMessageContent, 1);
+ setupMethod(ctx, classObj, "certificateInfo", &UtilitiesExtension::js_certificateInfo, 1);
+ setupMethod(ctx, classObj, "signingIdentities",
+ &UtilitiesExtension::js_signingIdentities, 1);
+ setupMethod(ctx, classObj, "msvcCompilerInfo",
+ &UtilitiesExtension::js_msvcCompilerInfo, 1);
+ setupMethod(ctx, classObj, "clangClCompilerInfo",
+ &UtilitiesExtension::js_clangClCompilerInfo, 1);
+ setupMethod(ctx, classObj, "installedMSVCs", &UtilitiesExtension::js_installedMSVCs, 1);
+ setupMethod(ctx, classObj, "installedClangCls",
+ &UtilitiesExtension::js_installedClangCls, 1);
+ setupMethod(ctx, classObj, "versionCompare", &UtilitiesExtension::js_versionCompare, 2);
+ setupMethod(ctx, classObj, "qmlTypeInfo", &UtilitiesExtension::js_qmlTypeInfo, 0);
+ setupMethod(ctx, classObj, "builtinExtensionNames",
+ &UtilitiesExtension::js_builtinExtensionNames, 0);
+ setupMethod(ctx, classObj, "isSharedLibrary", &UtilitiesExtension::js_isSharedLibrary, 1);
+ setupMethod(ctx, classObj, "getArchitecturesFromBinary",
+ &UtilitiesExtension::js_getArchitecturesFromBinary, 1);
+}
+
+JSValue UtilitiesExtension::js_canonicalArchitecture(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- const QScriptValue value = context->argument(0);
- if (value.isUndefined() || value.isNull())
- return value;
-
- if (context->argumentCount() == 1 && value.isString())
- return engine->toScriptValue(canonicalArchitecture(value.toString()));
-
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("canonicalArchitecture expects one argument of type string"));
+ try {
+ const auto arch = getArgument<QString>(ctx, "Utilities.canonicalArchitecture", argc, argv);
+ if (arch.isEmpty())
+ return makeJsString(ctx, {});
+ return makeJsString(ctx, canonicalArchitecture(arch));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue UtilitiesExtension::js_canonicalToolchain(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_canonicalToolchain(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- QStringList toolchain;
- for (int i = 0; i < context->argumentCount(); ++i)
- toolchain << context->argument(i).toString();
- return engine->toScriptValue(canonicalToolchain(toolchain));
+ try {
+ QStringList toolchain;
+ for (int i = 0; i < argc; ++i)
+ toolchain << fromArg<QString>(ctx, "Utilities.canonicalToolchain", i + 1, argv[i]);
+ return makeJsStringList(ctx, canonicalToolchain(toolchain));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
// copied from src/corelib/tools/qtools_p.h
@@ -331,74 +359,85 @@ static inline QString escapedString(const Char *begin, int length, bool isUnicod
return out;
}
-QScriptValue UtilitiesExtension::js_cStringQuote(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_cStringQuote(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("cStringQuote expects 1 argument"));
+ try {
+ const auto str = getArgument<QString>(ctx, "Utilities.cStringQuote", argc, argv);
+ return makeJsString(ctx, escapedString(
+ reinterpret_cast<const ushort *>(str.constData()), str.size()));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
- QString value = context->argument(0).toString();
- return engine->toScriptValue(escapedString(reinterpret_cast<const ushort *>(value.constData()), value.size()));
}
-QScriptValue UtilitiesExtension::js_getHash(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_getHash(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getHash expects 1 argument"));
+ try {
+ const QByteArray input = getArgument<QString>(ctx, "Utilities.getHash", argc, argv)
+ .toLatin1();
+ const QByteArray hash = QCryptographicHash::hash(input, QCryptographicHash::Sha1)
+ .toHex().left(16);
+ return makeJsString(ctx, QString::fromLatin1(hash));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
- const QByteArray input = context->argument(0).toString().toLatin1();
- const QByteArray hash
- = QCryptographicHash::hash(input, QCryptographicHash::Sha1).toHex().left(16);
- return engine->toScriptValue(QString::fromLatin1(hash));
}
-QScriptValue UtilitiesExtension::js_getNativeSetting(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_getNativeSetting(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() < 1 || context->argumentCount() > 3)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getNativeSetting expects between 1 and 3 arguments"));
- }
+ try {
+ const auto path = getArgument<QString>(ctx, "Utilities.getNativeSetting", argc, argv);
- QString key = context->argumentCount() > 1 ? context->argument(1).toString() : QString();
+ QString key;
+ if (argc > 1)
+ key = fromArg<QString>(ctx, "Utilities.getNativeSetting", 2, argv[1]);
- // We'll let empty string represent the default registry value
- if (HostOsInfo::isWindowsHost() && key.isEmpty())
- key = StringConstants::dot();
+ // QSettings will ignore the default value if an empty key is passed.
+ if (key.isEmpty()) {
+ key = HostOsInfo::isWindowsHost() ? StringConstants::dot() // default registry value
+ : QLatin1String("__dummy");
+ }
- QVariant defaultValue = context->argumentCount() > 2 ? context->argument(2).toVariant() : QVariant();
+ QVariant defaultValue;
+ if (argc > 2)
+ defaultValue = fromArg<QVariant>(ctx, "Utilities.getNativeSetting", 3, argv[2]);
- QSettings settings(context->argument(0).toString(), QSettings::NativeFormat);
- QVariant value = settings.value(key, defaultValue);
- return value.isNull() ? engine->undefinedValue() : engine->toScriptValue(value);
+ const QSettings settings(path, QSettings::NativeFormat);
+ const QVariant v = settings.value(key, defaultValue);
+ return v.isNull() ? JS_UNDEFINED : makeJsVariant(ctx, v);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue UtilitiesExtension::js_kernelVersion(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_kernelVersion(JSContext *ctx, JSValueConst, int, JSValueConst *)
{
- Q_UNUSED(context);
- return engine->toScriptValue(QSysInfo::kernelVersion());
+ return makeJsString(ctx, QSysInfo::kernelVersion());
}
-QScriptValue UtilitiesExtension::js_nativeSettingGroups(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_nativeSettingGroups(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() != 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("nativeSettingGroups expects 1 argument"));
+ try {
+ const auto path = getArgument<QString>(ctx, "Utilities.nativeSettingGroups", argc, argv);
+ QSettings settings(path, QSettings::NativeFormat);
+ return makeJsStringList(ctx, settings.childGroups());
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- QSettings settings(context->argument(0).toString(), QSettings::NativeFormat);
- return engine->toScriptValue(settings.childGroups());
}
-QScriptValue UtilitiesExtension::js_rfc1034identifier(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_rfc1034identifier(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("rfc1034Identifier expects 1 argument"));
- const QString identifier = context->argument(0).toString();
- return engine->toScriptValue(HostOsInfo::rfc1034Identifier(identifier));
+ try {
+ const auto identifier = getArgument<QString>(ctx, "Utilities.rfc1034identifier", argc, argv);
+ return makeJsString(ctx, HostOsInfo::rfc1034Identifier(identifier));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
/**
@@ -411,56 +450,55 @@ QScriptValue UtilitiesExtension::js_rfc1034identifier(QScriptContext *context,
* \note A provisioning profile is an S/MIME message whose contents are an XML property list,
* so this method can be used to read such files.
*/
-QScriptValue UtilitiesExtension::js_smimeMessageContent(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_smimeMessageContent(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX)
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("smimeMessageContent is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("smimeMessageContent is not available on this platform"));
#else
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("smimeMessageContent expects 1 argument"));
-
- const QString filePath = context->argument(0).toString();
- QFile file(filePath);
- if (!file.open(QIODevice::ReadOnly))
- return engine->undefinedValue();
-
- QByteArray content = smimeMessageContent(file.readAll());
- if (content.isEmpty())
- return engine->undefinedValue();
- return engine->toScriptValue(content);
+ try {
+ const QString filePath = getArgument<QString>(ctx, "Utilities.smimeMessageContent",
+ argc, argv);
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly))
+ return JS_UNDEFINED;
+
+ QByteArray content = smimeMessageContent(file.readAll());
+ if (content.isEmpty())
+ return JS_UNDEFINED;
+ return toJsValue(ctx, content);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
#endif
}
-QScriptValue UtilitiesExtension::js_certificateInfo(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_certificateInfo(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX)
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("certificateInfo is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("certificateInfo is not available on this platform"));
#else
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("certificateInfo expects 1 argument"));
- return engine->toScriptValue(certificateInfo(context->argument(0).toVariant().toByteArray()));
+ try {
+ const QVariant arg = getArgument<QVariant>(ctx, "Utilities.certificateInfo", argc, argv);
+ return toJsValue(ctx, certificateInfo(arg.toByteArray()));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
#endif
}
// Rough command line equivalent: security find-identity -p codesigning -v
-QScriptValue UtilitiesExtension::js_signingIdentities(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_signingIdentities(JSContext *ctx, JSValueConst, int, JSValueConst *)
{
#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX)
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("signingIdentities is not available on this platform"));
+ return throwError(ctx, QStringLiteral("signingIdentities is not available on this platform"));
#else
- Q_UNUSED(context);
- return engine->toScriptValue(identitiesProperties());
+ return makeJsVariantMap(ctx, identitiesProperties());
#endif
}
@@ -514,186 +552,164 @@ static std::pair<QVariantMap /*result*/, QString /*error*/> msvcCompilerInfoHelp
}
#endif
-QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_msvcCompilerInfo(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
#ifndef Q_OS_WIN
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("msvcCompilerInfo is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("msvcCompilerInfo is not available on this platform"));
#else
- if (Q_UNLIKELY(context->argumentCount() < 3))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("msvcCompilerInfo expects 3 arguments"));
-
- const QString compilerFilePath = context->argument(0).toString();
- const QString compilerLanguage = context->argument(1).toString();
- const QString sdkVersion =
- !context->argument(2).isNull() && !context->argument(2).isUndefined()
- ? context->argument(2).toString()
- : QString();
- MSVC::CompilerLanguage language;
- if (compilerLanguage == QStringLiteral("c"))
- language = MSVC::CLanguage;
- else if (compilerLanguage == StringConstants::cppLang())
- language = MSVC::CPlusPlusLanguage;
- else
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("msvcCompilerInfo expects \"c\" or \"cpp\" as its second argument"));
-
- const auto result = msvcCompilerInfoHelper(
- compilerFilePath,
- language,
- {},
- MSVC::architectureFromClPath(compilerFilePath),
- sdkVersion);
- if (result.first.isEmpty())
- return context->throwError(QScriptContext::UnknownError, result.second);
- return engine->toScriptValue(result.first);
+ try {
+ const auto args = getArguments<QString, QString, QString>(ctx, "Utilities.msvcCompilerInfo", argc, argv);
+ const QString compilerFilePath = std::get<0>(args);
+ const QString compilerLanguage = std::get<1>(args);
+ const QString sdkVersion = std::get<2>(args);
+ MSVC::CompilerLanguage language;
+ if (compilerLanguage == QStringLiteral("c"))
+ language = MSVC::CLanguage;
+ else if (compilerLanguage == StringConstants::cppLang())
+ language = MSVC::CPlusPlusLanguage;
+ else
+ throw Tr::tr("Utilities.msvcCompilerInfo expects \"c\" or \"cpp\" as its second argument");
+ const auto result = msvcCompilerInfoHelper(
+ compilerFilePath,
+ language,
+ {},
+ MSVC::architectureFromClPath(compilerFilePath),
+ sdkVersion);
+ if (result.first.isEmpty())
+ throw result.second;
+ return toJsValue(ctx, result.first);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
#endif
}
-QScriptValue UtilitiesExtension::js_clangClCompilerInfo(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_clangClCompilerInfo(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
#ifndef Q_OS_WIN
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("clangClCompilerInfo is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("clangClCompilerInfo is not available on this platform"));
#else
- if (Q_UNLIKELY(context->argumentCount() < 5))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("clangClCompilerInfo expects 5 arguments"));
+ try {
+ const auto args = getArguments<QString, QString, QString, QString, QString>(ctx, "Utilities.clangClCompilerInfo", argc, argv);
+ const QString compilerFilePath = std::get<0>(args);
- const QString compilerFilePath = context->argument(0).toString();
// architecture cannot be empty as vcvarsall.bat requires at least 1 arg, so fallback
// to host architecture if none is present
- QString arch = !context->argument(1).isNull() && !context->argument(1).isUndefined()
- ? context->argument(1).toString()
- : HostOsInfo::hostOSArchitecture();
- QString vcvarsallPath = context->argument(2).toString();
- const QString compilerLanguage =
- !context->argument(3).isNull() && !context->argument(3).isUndefined()
- ? context->argument(3).toString()
- : QString();
- const QString sdkVersion =
- !context->argument(4).isNull() && !context->argument(4).isUndefined()
- ? context->argument(4).toString()
- : QString();
+ QString arch = std::get<1>(args);
+ if (arch.isEmpty())
+ arch = HostOsInfo::hostOSArchitecture();
+
+ const QString vcvarsallPath = std::get<2>(args);
+ const QString compilerLanguage = std::get<3>(args);
+ const QString sdkVersion = std::get<4>(args);
MSVC::CompilerLanguage language;
if (compilerLanguage == QStringLiteral("c"))
language = MSVC::CLanguage;
else if (compilerLanguage == StringConstants::cppLang())
language = MSVC::CPlusPlusLanguage;
else
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("clangClCompilerInfo expects \"c\" or \"cpp\" as its fourth argument"));
+ throw Tr::tr("Utilities.clangClCompilerInfo expects \"c\" or \"cpp\" as its fourth argument");
const auto result = msvcCompilerInfoHelper(
compilerFilePath, language, vcvarsallPath, arch, sdkVersion);
if (result.first.isEmpty())
- return context->throwError(QScriptContext::UnknownError, result.second);
- return engine->toScriptValue(result.first);
+ throw result.second;
+ return toJsValue(ctx, result.first);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
#endif
}
-QScriptValue UtilitiesExtension::js_installedMSVCs(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_installedMSVCs(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
#ifndef Q_OS_WIN
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("installedMSVCs is not available on this platform"));
+ return throwError(ctx, QStringLiteral("Utilities.installedMSVCs is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
#else
- if (Q_UNLIKELY(context->argumentCount() != 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("installedMSVCs expects 1 arguments"));
+ try {
+ const QString hostArch = HostOsInfo::hostOSArchitecture();
+ QString preferredArch = getArgument<QString>(ctx, "Utilities.installedMSVCs", argc, argv);
+ if (preferredArch.isEmpty())
+ preferredArch = hostArch;
+
+ DummyLogSink dummySink;
+ Logger dummyLogger(&dummySink);
+ auto msvcs = MSVC::installedCompilers(dummyLogger);
+
+ const auto predicate = [&preferredArch, &hostArch](const MSVC &msvc)
+ {
+ auto archPair = MSVC::getHostTargetArchPair(msvc.architecture);
+ return archPair.first != hostArch || preferredArch != archPair.second;
+ };
+ Internal::removeIf(msvcs, predicate);
+ QVariantList result;
+ for (const auto &msvc: msvcs)
+ result.append(msvc.toVariantMap());
+ return makeJsVariantList(ctx, result);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- const auto value0 = context->argument(0);
- const auto hostArch = HostOsInfo::hostOSArchitecture();
- const auto preferredArch = !value0.isNull() && !value0.isUndefined()
- ? value0.toString()
- : hostArch;
-
- DummyLogSink dummySink;
- Logger dummyLogger(&dummySink);
- auto msvcs = MSVC::installedCompilers(dummyLogger);
-
- const auto predicate = [&preferredArch, &hostArch](const MSVC &msvc)
- {
- auto archPair = MSVC::getHostTargetArchPair(msvc.architecture);
- return archPair.first != hostArch || preferredArch != archPair.second;
- };
- Internal::removeIf(msvcs, predicate);
- QVariantList result;
- for (const auto &msvc: msvcs)
- result.append(msvc.toVariantMap());
- return engine->toScriptValue(result);
#endif
}
-QScriptValue UtilitiesExtension::js_installedClangCls(
- QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_installedClangCls(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
#ifndef Q_OS_WIN
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("installedClangCls is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("Utilities.installedClangCls is not available on this platform"));
#else
- if (Q_UNLIKELY(context->argumentCount() != 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("installedClangCls expects 1 arguments"));
+ try {
+ const QString path = getArgument<QString>(ctx, "Utilities.installedClangCls", argc, argv);
+ DummyLogSink dummySink;
+ Logger dummyLogger(&dummySink);
+ auto compilers = ClangClInfo::installedCompilers({path}, dummyLogger);
+ QVariantList result;
+ for (const auto &compiler: compilers)
+ result.append(compiler.toVariantMap());
+ return makeJsVariantList(ctx, result);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- const auto value0 = context->argument(0);
- const auto path = !value0.isNull() && !value0.isUndefined() ? value0.toString() : QString();
-
- DummyLogSink dummySink;
- Logger dummyLogger(&dummySink);
- auto compilers = ClangClInfo::installedCompilers({path}, dummyLogger);
- QVariantList result;
- for (const auto &compiler: compilers)
- result.append(compiler.toVariantMap());
- return engine->toScriptValue(result);
#endif
}
-QScriptValue UtilitiesExtension::js_versionCompare(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_versionCompare(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (context->argumentCount() == 2) {
- const QScriptValue value1 = context->argument(0);
- const QScriptValue value2 = context->argument(1);
- if (value1.isString() && value2.isString()) {
- const auto a = Version::fromString(value1.toString());
- const auto b = Version::fromString(value2.toString());
- return engine->toScriptValue(compare(a, b));
- }
- }
-
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("versionCompare expects two arguments of type string"));
+ try {
+ const auto args = getArguments<QString, QString>(ctx, "Utilities.versionCompare",
+ argc, argv);
+ const auto a = Version::fromString(std::get<0>(args));
+ const auto b = Version::fromString(std::get<1>(args));
+ return JS_NewInt32(ctx, compare(a, b));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue UtilitiesExtension::js_qmlTypeInfo(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_qmlTypeInfo(JSContext *ctx, JSValueConst, int, JSValueConst *)
{
- Q_UNUSED(context);
- return engine->toScriptValue(QString::fromStdString(qbs::LanguageInfo::qmlTypeInfo()));
+ return makeJsString(ctx, QString::fromStdString(qbs::LanguageInfo::qmlTypeInfo()));
}
-QScriptValue UtilitiesExtension::js_builtinExtensionNames(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_builtinExtensionNames(JSContext *ctx, JSValueConst,
+ int, JSValueConst *)
{
- Q_UNUSED(context);
- return engine->toScriptValue(JsExtensions::extensionNames());
+ return makeJsStringList(ctx, JsExtensions::extensionNames());
}
-QScriptValue UtilitiesExtension::js_isSharedLibrary(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_isSharedLibrary(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (context->argumentCount() == 1) {
- const QScriptValue value = context->argument(0);
- if (value.isString())
- return engine->toScriptValue(QLibrary::isLibrary(value.toString()));
- }
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("isSharedLibrary expects one argument of type string"));
+ try {
+ const auto fileName = getArgument<QString>(ctx, "Utilities.isSharedLibrary", argc, argv);
+ return JS_NewBool(ctx, QLibrary::isLibrary(fileName));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
#ifdef __APPLE__
@@ -891,88 +907,32 @@ static QStringList detectMachOArchs(QIODevice *device)
}
#endif
-QScriptValue UtilitiesExtension::js_getArchitecturesFromBinary(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_getArchitecturesFromBinary(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (context->argumentCount() != 1) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getArchitecturesFromBinary expects exactly one argument"));
- }
- const QScriptValue arg = context->argument(0);
- if (!arg.isString()) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getArchitecturesFromBinary expects a string argument"));
- }
- QStringList archs;
+ try {
+ const auto path = getArgument<QString>(ctx, "Utilities.getArchitecturesFromBinary",
+ argc, argv);
+ QStringList archs;
#ifdef __APPLE__
- QFile file(arg.toString());
- if (!file.open(QIODevice::ReadOnly)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("Failed to open file '%1': %2")
- .arg(file.fileName(), file.errorString()));
- }
- archs = detectMachOArchs(&file);
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ throw Tr::tr("In Utilities.getArchitecturesFromBinary: "
+ "Failed to open file '%1': %2")
+ .arg(file.fileName(), file.errorString());
+ }
+ archs = detectMachOArchs(&file);
+#else
+ Q_UNUSED(path)
#endif // __APPLE__
- return engine->toScriptValue(archs);
+ return makeJsStringList(ctx, archs);
+ } catch (const QString &error) { return throwError(ctx, error); }
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionUtilities(QScriptValue extensionObject)
-{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue environmentObj = engine->newQMetaObject(&UtilitiesExtension::staticMetaObject,
- engine->newFunction(&UtilitiesExtension::js_ctor));
- environmentObj.setProperty(QStringLiteral("canonicalArchitecture"),
- engine->newFunction(UtilitiesExtension::js_canonicalArchitecture, 1));
- environmentObj.setProperty(QStringLiteral("canonicalPlatform"),
- engine->newFunction(UtilitiesExtension::js_canonicalPlatform, 1));
- environmentObj.setProperty(QStringLiteral("canonicalTargetArchitecture"),
- engine->newFunction(
- UtilitiesExtension::js_canonicalTargetArchitecture, 4));
- environmentObj.setProperty(QStringLiteral("canonicalToolchain"),
- engine->newFunction(UtilitiesExtension::js_canonicalToolchain));
- environmentObj.setProperty(QStringLiteral("cStringQuote"),
- engine->newFunction(UtilitiesExtension::js_cStringQuote, 1));
- environmentObj.setProperty(QStringLiteral("getHash"),
- engine->newFunction(UtilitiesExtension::js_getHash, 1));
- environmentObj.setProperty(QStringLiteral("getNativeSetting"),
- engine->newFunction(UtilitiesExtension::js_getNativeSetting, 3));
- environmentObj.setProperty(QStringLiteral("kernelVersion"),
- engine->newFunction(UtilitiesExtension::js_kernelVersion, 0));
- environmentObj.setProperty(QStringLiteral("nativeSettingGroups"),
- engine->newFunction(UtilitiesExtension::js_nativeSettingGroups, 1));
- environmentObj.setProperty(QStringLiteral("rfc1034Identifier"),
- engine->newFunction(UtilitiesExtension::js_rfc1034identifier, 1));
- environmentObj.setProperty(QStringLiteral("smimeMessageContent"),
- engine->newFunction(UtilitiesExtension::js_smimeMessageContent, 1));
- environmentObj.setProperty(QStringLiteral("certificateInfo"),
- engine->newFunction(UtilitiesExtension::js_certificateInfo, 1));
- environmentObj.setProperty(QStringLiteral("signingIdentities"),
- engine->newFunction(UtilitiesExtension::js_signingIdentities, 0));
- environmentObj.setProperty(QStringLiteral("msvcCompilerInfo"),
- engine->newFunction(UtilitiesExtension::js_msvcCompilerInfo, 1));
- environmentObj.setProperty(QStringLiteral("clangClCompilerInfo"),
- engine->newFunction(UtilitiesExtension::js_clangClCompilerInfo, 1));
- environmentObj.setProperty(QStringLiteral("installedMSVCs"),
- engine->newFunction(UtilitiesExtension::js_installedMSVCs, 1));
- environmentObj.setProperty(QStringLiteral("installedClangCls"),
- engine->newFunction(UtilitiesExtension::js_installedClangCls, 1));
- environmentObj.setProperty(QStringLiteral("versionCompare"),
- engine->newFunction(UtilitiesExtension::js_versionCompare, 2));
- environmentObj.setProperty(QStringLiteral("qmlTypeInfo"),
- engine->newFunction(UtilitiesExtension::js_qmlTypeInfo, 0));
- environmentObj.setProperty(QStringLiteral("builtinExtensionNames"),
- engine->newFunction(UtilitiesExtension::js_builtinExtensionNames, 0));
- environmentObj.setProperty(QStringLiteral("isSharedLibrary"),
- engine->newFunction(UtilitiesExtension::js_isSharedLibrary, 1));
- environmentObj.setProperty(QStringLiteral("getArchitecturesFromBinary"),
- engine->newFunction(UtilitiesExtension::js_getArchitecturesFromBinary, 1));
- extensionObject.setProperty(QStringLiteral("Utilities"), environmentObj);
-}
-
-Q_DECLARE_METATYPE(qbs::Internal::UtilitiesExtension *)
-
-#include "utilitiesextension.moc"
+void initializeJsExtensionUtilities(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
+{
+ qbs::Internal::UtilitiesExtension::registerClass(engine, extensionObject);
+}
diff --git a/src/lib/corelib/language/evaluationdata.h b/src/lib/corelib/language/evaluationdata.h
deleted file mode 100644
index 791b2f234..000000000
--- a/src/lib/corelib/language/evaluationdata.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QBS_EVALUATIONDATA_H
-#define QBS_EVALUATIONDATA_H
-
-#include <QtCore/qhash.h>
-#include <QtCore/qvariant.h>
-
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-
-namespace qbs {
-namespace Internal {
-
-class Evaluator;
-class Item;
-
-class EvaluationData
-{
-public:
- Evaluator *evaluator = nullptr;
- const Item *item = nullptr;
- mutable QHash<QScriptString, QScriptValue> valueCache;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_EVALUATIONDATA_H
diff --git a/src/lib/corelib/language/evaluator.cpp b/src/lib/corelib/language/evaluator.cpp
index 84272af2f..5252f06be 100644
--- a/src/lib/corelib/language/evaluator.cpp
+++ b/src/lib/corelib/language/evaluator.cpp
@@ -39,18 +39,19 @@
#include "evaluator.h"
-#include "evaluationdata.h"
-#include "evaluatorscriptclass.h"
#include "filecontext.h"
#include "filetags.h"
#include "item.h"
#include "scriptengine.h"
#include "value.h"
+#include <quickjs.h>
+
#include <buildgraph/buildgraph.h>
#include <jsextensions/jsextensions.h>
#include <logging/translator.h>
#include <tools/error.h>
+#include <tools/fileinfo.h>
#include <tools/scripttools.h>
#include <tools/qbsassert.h>
#include <tools/qttools.h>
@@ -61,27 +62,61 @@
namespace qbs {
namespace Internal {
+class EvaluationData
+{
+public:
+ Evaluator *evaluator = nullptr;
+ const Item *item = nullptr;
+ mutable QHash<QString, JSValue> valueCache;
+};
+
+static void convertToPropertyType_impl(ScriptEngine *engine,
+ const QString &pathPropertiesBaseDir, const Item *item,
+ const PropertyDeclaration& decl,
+ const CodeLocation &location, JSValue &v);
+static int getEvalPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj);
+static int getEvalProperty(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop);
+
+static bool debugProperties = false;
+
Evaluator::Evaluator(ScriptEngine *scriptEngine)
: m_scriptEngine(scriptEngine)
- , m_scriptClass(new EvaluatorScriptClass(scriptEngine))
+ , m_scriptClass(scriptEngine->registerClass("Evaluator", nullptr, nullptr, JS_UNDEFINED,
+ getEvalPropertyNames, getEvalProperty))
{
+ scriptEngine->registerEvaluator(this);
}
Evaluator::~Evaluator()
{
- for (const auto &data : qAsConst(m_scriptValueMap))
- delete attachedPointer<EvaluationData>(data);
- delete m_scriptClass;
+ Set<JSValue> valuesToFree;
+ for (const auto &data : qAsConst(m_scriptValueMap)) {
+ const auto evalData = attachedPointer<EvaluationData>(data, m_scriptClass);
+ valuesToFree << data;
+ for (const JSValue cachedValue : evalData->valueCache)
+ JS_FreeValue(m_scriptEngine->context(), cachedValue);
+ delete evalData;
+ }
+ for (const auto &scopes : qAsConst(m_fileContextScopesMap)) {
+ valuesToFree << scopes.fileScope;
+ valuesToFree << scopes.importScope;
+ }
+ for (const JSValue v : qAsConst(valuesToFree)) {
+ JS_FreeValue(m_scriptEngine->context(), v);
+ }
+ m_scriptEngine->unregisterEvaluator(this);
}
-QScriptValue Evaluator::property(const Item *item, const QString &name)
+JSValue Evaluator::property(const Item *item, const QString &name)
{
- return scriptValue(item).property(name);
+ return getJsProperty(m_scriptEngine->context(), scriptValue(item), name);
}
-QScriptValue Evaluator::value(const Item *item, const QString &name, bool *propertyWasSet)
+JSValue Evaluator::value(const Item *item, const QString &name, bool *propertyWasSet)
{
- QScriptValue v;
+ JSValue v;
evaluateProperty(&v, item, name, propertyWasSet);
return v;
}
@@ -89,16 +124,19 @@ QScriptValue Evaluator::value(const Item *item, const QString &name, bool *prope
bool Evaluator::boolValue(const Item *item, const QString &name,
bool *propertyWasSet)
{
- return value(item, name, propertyWasSet).toBool();
+ const ScopedJsValue sv(m_scriptEngine->context(), value(item, name, propertyWasSet));
+ return JS_ToBool(m_scriptEngine->context(), sv);
}
int Evaluator::intValue(const Item *item, const QString &name, int defaultValue,
bool *propertyWasSet)
{
- QScriptValue v;
+ JSValue v;
if (!evaluateProperty(&v, item, name, propertyWasSet))
return defaultValue;
- return v.toInt32();
+ qint32 n;
+ JS_ToInt32(m_scriptEngine->context(), &n, v);
+ return n;
}
FileTags Evaluator::fileTagsValue(const Item *item, const QString &name, bool *propertySet)
@@ -109,24 +147,27 @@ FileTags Evaluator::fileTagsValue(const Item *item, const QString &name, bool *p
QString Evaluator::stringValue(const Item *item, const QString &name,
const QString &defaultValue, bool *propertyWasSet)
{
- QScriptValue v;
+ JSValue v;
if (!evaluateProperty(&v, item, name, propertyWasSet))
return defaultValue;
- return v.toString();
+ QString str = getJsString(m_scriptEngine->context(), v);
+ JS_FreeValue(m_scriptEngine->context(), v);
+ return str;
}
-static QStringList toStringList(const QScriptValue &scriptValue)
+static QStringList toStringList(ScriptEngine *engine, const JSValue &scriptValue)
{
- if (scriptValue.isString())
- return {scriptValue.toString()};
- if (scriptValue.isArray()) {
+ if (JS_IsString(scriptValue))
+ return {getJsString(engine->context(), scriptValue)};
+ if (JS_IsArray(engine->context(), scriptValue)) {
QStringList lst;
int i = 0;
for (;;) {
- QScriptValue elem = scriptValue.property(i++);
- if (!elem.isValid())
+ JSValue elem = JS_GetPropertyUint32(engine->context(), scriptValue, i++);
+ if (JS_IsUndefined(elem))
break;
- lst.push_back(elem.toString());
+ lst.push_back(getJsString(engine->context(), elem));
+ JS_FreeValue(engine->context(), elem);
}
return lst;
}
@@ -142,13 +183,13 @@ QStringList Evaluator::stringListValue(const Item *item, const QString &name, bo
std::optional<QStringList> Evaluator::optionalStringListValue(
const Item *item, const QString &name, bool *propertyWasSet)
{
- QScriptValue v = property(item, name);
- handleEvaluationError(item, name, v);
+ const ScopedJsValue v(m_scriptEngine->context(), property(item, name));
+ handleEvaluationError(item, name);
if (propertyWasSet)
*propertyWasSet = isNonDefaultValue(item, name);
- if (v.isUndefined())
+ if (JS_IsUndefined(v))
return std::nullopt;
- return toStringList(v);
+ return toStringList(m_scriptEngine, v);
}
bool Evaluator::isNonDefaultValue(const Item *item, const QString &name) const
@@ -159,15 +200,15 @@ bool Evaluator::isNonDefaultValue(const Item *item, const QString &name) const
}
void Evaluator::convertToPropertyType(const PropertyDeclaration &decl, const CodeLocation &loc,
- QScriptValue &v)
+ JSValue &v)
{
- m_scriptClass->convertToPropertyType(decl, loc, v);
+ convertToPropertyType_impl(engine(), QString(), nullptr, decl, loc, v);
}
-QScriptValue Evaluator::scriptValue(const Item *item)
+JSValue Evaluator::scriptValue(const Item *item)
{
- QScriptValue &scriptValue = m_scriptValueMap[item];
- if (scriptValue.isObject()) {
+ JSValue &scriptValue = m_scriptValueMap[item];
+ if (JS_IsObject(scriptValue)) {
// already initialized
return scriptValue;
}
@@ -177,109 +218,720 @@ QScriptValue Evaluator::scriptValue(const Item *item)
edata->item = item;
edata->item->setObserver(this);
- scriptValue = m_scriptEngine->newObject(m_scriptClass);
+ scriptValue = JS_NewObjectClass(m_scriptEngine->context(), m_scriptClass);
attachPointerTo(scriptValue, edata);
return scriptValue;
}
void Evaluator::onItemPropertyChanged(Item *item)
{
- auto data = attachedPointer<EvaluationData>(m_scriptValueMap.value(item));
- if (data)
+ const auto data = attachedPointer<EvaluationData>(m_scriptValueMap.value(item),
+ m_scriptEngine->dataWithPtrClass());
+ if (data) {
+ for (const auto value : qAsConst(data->valueCache))
+ JS_FreeValue(m_scriptEngine->context(), value);
data->valueCache.clear();
+ }
}
-void Evaluator::handleEvaluationError(const Item *item, const QString &name,
- const QScriptValue &scriptValue)
+void Evaluator::handleEvaluationError(const Item *item, const QString &name)
{
- throwOnEvaluationError(m_scriptEngine, scriptValue, [&item, &name] () {
+ throwOnEvaluationError(m_scriptEngine, [&item, &name] () {
const ValueConstPtr &value = item->property(name);
return value ? value->location() : CodeLocation();
});
}
-void Evaluator::setPathPropertiesBaseDir(const QString &dirPath)
-{
- m_scriptClass->setPathPropertiesBaseDir(dirPath);
-}
-
-void Evaluator::clearPathPropertiesBaseDir()
-{
- m_scriptClass->clearPathPropertiesBaseDir();
-}
-
-bool Evaluator::evaluateProperty(QScriptValue *result, const Item *item, const QString &name,
+bool Evaluator::evaluateProperty(JSValue *result, const Item *item, const QString &name,
bool *propertyWasSet)
{
*result = property(item, name);
- handleEvaluationError(item, name, *result);
+ ScopedJsValue valMgr(m_scriptEngine->context(), *result);
+ handleEvaluationError(item, name);
+ valMgr.release();
if (propertyWasSet)
*propertyWasSet = isNonDefaultValue(item, name);
- return result->isValid() && !result->isUndefined();
+ return !JS_IsUndefined(*result);
}
Evaluator::FileContextScopes Evaluator::fileContextScopes(const FileContextConstPtr &file)
{
FileContextScopes &result = m_fileContextScopesMap[file];
- if (!result.fileScope.isObject()) {
+ if (!JS_IsObject(result.fileScope)) {
if (file->idScope())
result.fileScope = scriptValue(file->idScope());
else
result.fileScope = m_scriptEngine->newObject();
- result.fileScope.setProperty(StringConstants::filePathGlobalVar(), file->filePath());
- result.fileScope.setProperty(StringConstants::pathGlobalVar(), file->dirPath());
+ setJsProperty(m_scriptEngine->context(), result.fileScope,
+ StringConstants::filePathGlobalVar(), file->filePath());
+ setJsProperty(m_scriptEngine->context(), result.fileScope,
+ StringConstants::pathGlobalVar(), file->dirPath());
}
- if (!result.importScope.isObject()) {
+ if (!JS_IsObject(result.importScope)) {
try {
result.importScope = m_scriptEngine->newObject();
setupScriptEngineForFile(m_scriptEngine, file, result.importScope,
ObserveMode::Enabled);
} catch (const ErrorInfo &e) {
- result.importScope = m_scriptEngine->currentContext()->throwError(e.toString());
+ result.importScope = throwError(m_scriptEngine->context(), e.toString());
}
}
return result;
}
-void Evaluator::setCachingEnabled(bool enabled)
+void throwOnEvaluationError(ScriptEngine *engine,
+ const std::function<CodeLocation()> &provideFallbackCodeLocation)
{
- m_scriptClass->setValueCacheEnabled(enabled);
+ if (JsException ex = engine->checkAndClearException(provideFallbackCodeLocation()))
+ throw ex.toErrorInfo();
}
-PropertyDependencies Evaluator::propertyDependencies() const
+static void makeTypeError(ScriptEngine *engine, const ErrorInfo &error, JSValue &v)
{
- return m_scriptClass->propertyDependencies();
+ v = throwError(engine->context(), error.toString());
}
-void Evaluator::clearPropertyDependencies()
+static void makeTypeError(ScriptEngine *engine, const PropertyDeclaration &decl,
+ const CodeLocation &location, JSValue &v)
{
- m_scriptClass->clearPropertyDependencies();
+ const ErrorInfo error(Tr::tr("Value assigned to property '%1' does not have type '%2'.")
+ .arg(decl.name(), decl.typeString()), location);
+ makeTypeError(engine, error, v);
}
-void throwOnEvaluationError(ScriptEngine *engine, const QScriptValue &scriptValue,
- const std::function<CodeLocation()> &provideFallbackCodeLocation)
+static QString overriddenSourceDirectory(const Item *item, const QString &defaultValue)
{
- if (Q_LIKELY(!engine->hasErrorOrException(scriptValue)))
+ const VariantValuePtr v = item->variantProperty
+ (StringConstants::qbsSourceDirPropertyInternal());
+ return v ? v->value().toString() : defaultValue;
+}
+
+static void convertToPropertyType_impl(ScriptEngine *engine,
+ const QString &pathPropertiesBaseDir, const Item *item,
+ const PropertyDeclaration& decl,
+ const CodeLocation &location, JSValue &v)
+{
+ JSContext * const ctx = engine->context();
+ if (JS_IsUndefined(v) || JS_IsError(ctx, v) || JS_IsException(v))
return;
- QString message;
- QString filePath;
- int line = -1;
- const QScriptValue value = scriptValue.isError() ? scriptValue
- : engine->uncaughtException();
- if (value.isError()) {
- QScriptValue v = value.property(QStringLiteral("message"));
- if (v.isString())
- message = v.toString();
- v = value.property(StringConstants::fileNameProperty());
- if (v.isString())
- filePath = v.toString();
- v = value.property(QStringLiteral("lineNumber"));
- if (v.isNumber())
- line = v.toInt32();
- throw ErrorInfo(message, CodeLocation(filePath, line, -1, false));
- }
- message = value.toString();
- throw ErrorInfo(message, provideFallbackCodeLocation());
+ QString srcDir;
+ QString actualBaseDir;
+ if (item && !pathPropertiesBaseDir.isEmpty()) {
+ const VariantValueConstPtr itemSourceDir
+ = item->variantProperty(QStringLiteral("sourceDirectory"));
+ actualBaseDir = itemSourceDir ? itemSourceDir->value().toString() : pathPropertiesBaseDir;
+ }
+ switch (decl.type()) {
+ case PropertyDeclaration::UnknownType:
+ case PropertyDeclaration::Variant:
+ break;
+ case PropertyDeclaration::Boolean:
+ if (!JS_IsBool(v))
+ v = JS_NewBool(ctx, JS_ToBool(ctx, v));
+ break;
+ case PropertyDeclaration::Integer:
+ if (!JS_IsNumber(v))
+ makeTypeError(engine, decl, location, v);
+ break;
+ case PropertyDeclaration::Path:
+ {
+ if (!JS_IsString(v)) {
+ makeTypeError(engine, decl, location, v);
+ break;
+ }
+ const QString srcDir = item ? overriddenSourceDirectory(item, actualBaseDir)
+ : pathPropertiesBaseDir;
+ if (!srcDir.isEmpty()) {
+ v = engine->toScriptValue(QDir::cleanPath(FileInfo::resolvePath(srcDir,
+ getJsString(ctx, v))));
+ JS_FreeValue(ctx, v);
+ }
+ break;
+ }
+ case PropertyDeclaration::String:
+ if (!JS_IsString(v))
+ makeTypeError(engine, decl, location, v);
+ break;
+ case PropertyDeclaration::PathList:
+ srcDir = item ? overriddenSourceDirectory(item, actualBaseDir)
+ : pathPropertiesBaseDir;
+ // Fall-through.
+ case PropertyDeclaration::StringList:
+ {
+ if (!JS_IsArray(ctx, v)) {
+ JSValue x = engine->newArray(1, JsValueOwner::ScriptEngine);
+ JS_SetPropertyUint32(ctx, x, 0, JS_DupValue(ctx, v));
+ v = x;
+ }
+ const quint32 c = getJsIntProperty(ctx, v, StringConstants::lengthProperty());
+ for (quint32 i = 0; i < c; ++i) {
+ const ScopedJsValue elem(ctx, JS_GetPropertyUint32(ctx, v, i));
+ if (JS_IsUndefined(elem)) {
+ ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' is undefined. "
+ "String expected.").arg(i).arg(decl.name()), location);
+ makeTypeError(engine, error, v);
+ break;
+ }
+ if (JS_IsNull(elem)) {
+ ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' is null. "
+ "String expected.").arg(i).arg(decl.name()), location);
+ makeTypeError(engine, error, v);
+ break;
+ }
+ if (!JS_IsString(elem)) {
+ ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' does not have "
+ "string type.").arg(i).arg(decl.name()), location);
+ makeTypeError(engine, error, v);
+ break;
+ }
+ if (srcDir.isEmpty())
+ continue;
+ const JSValue newElem = engine->toScriptValue(
+ QDir::cleanPath(FileInfo::resolvePath(srcDir, getJsString(ctx, elem))));
+ JS_SetPropertyUint32(ctx, v, i, newElem);
+ }
+ break;
+ }
+ case PropertyDeclaration::VariantList:
+ if (!JS_IsArray(ctx, v)) {
+ JSValue x = engine->newArray(1, JsValueOwner::ScriptEngine);
+ JS_SetPropertyUint32(ctx, x, 0, JS_DupValue(ctx, v));
+ v = x;
+ }
+ break;
+ }
+}
+
+static int getEvalPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, JSValue obj)
+{
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const Evaluator * const evaluator = engine->evaluator();
+ const auto data = attachedPointer<EvaluationData>(obj, evaluator->classId());
+ if (!data)
+ return -1;
+ const Item::PropertyMap &map = data->item->properties();
+ *plen = map.size();
+ if (!map.isEmpty()) {
+ *ptab = reinterpret_cast<JSPropertyEnum *>(js_malloc(ctx, *plen * sizeof **ptab));
+ JSPropertyEnum *entry = *ptab;
+ for (auto it = map.cbegin(); it != map.cend(); ++it, ++entry) {
+ entry->atom = JS_NewAtom(ctx, it.key().toUtf8().constData());
+ entry->is_enumerable = 1;
+ }
+ } else {
+ *ptab = nullptr;
+ }
+ return 0;
+}
+
+class PropertyStackManager
+{
+public:
+ PropertyStackManager(const Item *itemOfProperty, const QString &name, const Value *value,
+ std::stack<QualifiedId> &requestedProperties,
+ PropertyDependencies &propertyDependencies)
+ : m_requestedProperties(requestedProperties)
+ {
+ if (value->type() == Value::JSSourceValueType
+ && (itemOfProperty->type() == ItemType::ModuleInstance
+ || itemOfProperty->type() == ItemType::Module
+ || itemOfProperty->type() == ItemType::Export)) {
+ const VariantValueConstPtr varValue
+ = itemOfProperty->variantProperty(StringConstants::nameProperty());
+ if (!varValue)
+ return;
+ m_stackUpdate = true;
+ const QualifiedId fullPropName
+ = QualifiedId::fromString(varValue->value().toString()) << name;
+ if (!requestedProperties.empty())
+ propertyDependencies[fullPropName].insert(requestedProperties.top());
+ m_requestedProperties.push(fullPropName);
+ }
+ }
+
+ ~PropertyStackManager()
+ {
+ if (m_stackUpdate)
+ m_requestedProperties.pop();
+ }
+
+private:
+ std::stack<QualifiedId> &m_requestedProperties;
+ bool m_stackUpdate = false;
+};
+
+class SVConverter : ValueHandler
+{
+ ScriptEngine * const engine;
+ const JSValue * const object;
+ Value * const valuePtr;
+ const Item * const itemOfProperty;
+ const QString * const propertyName;
+ const EvaluationData * const data;
+ JSValue * const result;
+ JSValueList scopeChain;
+ char pushedScopesCount;
+
+public:
+
+ SVConverter(ScriptEngine *engine, const JSValue *obj, const ValuePtr &v,
+ const Item *_itemOfProperty, const QString *propertyName,
+ const EvaluationData *data, JSValue *result)
+ : engine(engine)
+ , object(obj)
+ , valuePtr(v.get())
+ , itemOfProperty(_itemOfProperty)
+ , propertyName(propertyName)
+ , data(data)
+ , result(result)
+ , pushedScopesCount(0)
+ {
+ }
+
+ void start()
+ {
+ valuePtr->apply(this);
+ }
+
+private:
+ friend class AutoScopePopper;
+
+ class AutoScopePopper
+ {
+ public:
+ AutoScopePopper(SVConverter *converter)
+ : m_converter(converter)
+ {
+ }
+
+ ~AutoScopePopper()
+ {
+ m_converter->popScopes();
+ }
+
+ private:
+ SVConverter *m_converter;
+ };
+
+ void setupConvenienceProperty(const QString &conveniencePropertyName, JSValue *extraScope,
+ const JSValue &scriptValue)
+ {
+ if (!JS_IsObject(*extraScope))
+ *extraScope = engine->newObject();
+ const PropertyDeclaration::Type type
+ = itemOfProperty->propertyDeclaration(*propertyName).type();
+ const bool isArray = type == PropertyDeclaration::StringList
+ || type == PropertyDeclaration::PathList
+ || type == PropertyDeclaration::Variant // TODO: Why?
+ || type == PropertyDeclaration::VariantList;
+ JSValue valueToSet = JS_DupValue(engine->context(), scriptValue);
+ if (isArray && JS_IsUndefined(valueToSet))
+ valueToSet = engine->newArray(0, JsValueOwner::Caller);
+ setJsProperty(engine->context(), *extraScope, conveniencePropertyName, valueToSet);
+ }
+
+ std::pair<JSValue, bool> createExtraScope(const JSSourceValue *value, Item *outerItem,
+ JSValue *outerScriptValue)
+ {
+ std::pair<JSValue, bool> result;
+ auto &extraScope = result.first;
+ result.second = true;
+ if (value->sourceUsesBase()) {
+ JSValue baseValue = JS_UNDEFINED;
+ if (value->baseValue()) {
+ SVConverter converter(engine, object, value->baseValue(), itemOfProperty,
+ propertyName, data, &baseValue);
+ converter.start();
+ }
+ setupConvenienceProperty(StringConstants::baseVar(), &extraScope, baseValue);
+ }
+ if (value->sourceUsesOuter()) {
+ JSValue v = JS_UNDEFINED;
+ bool doSetup = false;
+ if (outerItem) {
+ v = data->evaluator->property(outerItem, *propertyName);
+ if (JsException ex = engine->checkAndClearException({})) {
+ extraScope = engine->throwError(ex.toErrorInfo().toString());
+ result.second = false;
+ return result;
+ }
+ doSetup = true;
+ JS_FreeValue(engine->context(), v);
+ } else if (outerScriptValue) {
+ doSetup = true;
+ v = *outerScriptValue;
+ }
+ if (doSetup)
+ setupConvenienceProperty(StringConstants::outerVar(), &extraScope, v);
+ }
+ if (value->sourceUsesOriginal()) {
+ JSValue originalValue = JS_UNDEFINED;
+ ScopedJsValue originalMgr(engine->context(), JS_UNDEFINED);
+ if (data->item->propertyDeclaration(*propertyName).isScalar()) {
+ const Item *item = itemOfProperty;
+ if (item->type() == ItemType::Module || item->type() == ItemType::Export) {
+ const QString errorMessage = Tr::tr("The special value 'original' cannot "
+ "be used on the right-hand side of a property declaration.");
+ extraScope = throwError(engine->context(), errorMessage);
+ result.second = false;
+ return result;
+ }
+
+ // TODO: Provide a dedicated item type for not-yet-instantiated things that
+ // look like module instances in the AST visitor.
+ if (item->type() == ItemType::ModuleInstance
+ && !item->hasProperty(StringConstants::presentProperty())) {
+ const QString errorMessage = Tr::tr("Trying to assign property '%1' "
+ "on something that is not a module.").arg(*propertyName);
+ extraScope = throwError(engine->context(), errorMessage);
+ result.second = false;
+ return result;
+ }
+
+ while (item->type() == ItemType::ModuleInstance)
+ item = item->prototype();
+ if (item->type() != ItemType::Module && item->type() != ItemType::Export) {
+ const QString errorMessage = Tr::tr("The special value 'original' can only "
+ "be used with module properties.");
+ extraScope = throwError(engine->context(), errorMessage);
+ result.second = false;
+ return result;
+ }
+ const ValuePtr v = item->property(*propertyName);
+
+ // This can happen when resolving shadow products. The error will be ignored
+ // in that case.
+ if (!v) {
+ const QString errorMessage = Tr::tr("Error setting up 'original'.");
+ extraScope = throwError(engine->context(), errorMessage);
+ result.second = false;
+ return result;
+ }
+
+ SVConverter converter(engine, object, v, item, propertyName, data, &originalValue);
+ converter.start();
+ } else {
+ originalValue = engine->newArray(0, JsValueOwner::Caller);
+ originalMgr.setValue(originalValue);
+ }
+ setupConvenienceProperty(StringConstants::originalVar(), &extraScope, originalValue);
+ }
+ return result;
+ }
+
+ void pushScope(const JSValue &scope)
+ {
+ if (JS_IsObject(scope)) {
+ scopeChain << scope;
+ ++pushedScopesCount;
+ }
+ }
+
+ void pushItemScopes(const Item *item)
+ {
+ const Item *scope = item->scope();
+ if (scope) {
+ pushItemScopes(scope);
+ pushScope(data->evaluator->scriptValue(scope));
+ }
+ }
+
+ void popScopes()
+ {
+ for (; pushedScopesCount; --pushedScopesCount)
+ scopeChain.pop_back();
+ }
+
+ void handle(JSSourceValue *value) override
+ {
+ JSValue outerScriptValue = JS_UNDEFINED;
+ for (const JSSourceValue::Alternative &alternative : value->alternatives()) {
+ if (alternative.value->sourceUsesOuter()
+ && !data->item->outerItem()
+ && JS_IsUndefined(outerScriptValue)) {
+ JSSourceValueEvaluationResult sver = evaluateJSSourceValue(value, nullptr);
+ if (sver.hasError) {
+ *result = sver.scriptValue;
+ return;
+ }
+ outerScriptValue = sver.scriptValue;
+ }
+ JSSourceValueEvaluationResult sver = evaluateJSSourceValue(alternative.value.get(),
+ data->item->outerItem(),
+ &alternative,
+ value, &outerScriptValue);
+ if (!sver.tryNextAlternative || sver.hasError) {
+ *result = sver.scriptValue;
+ return;
+ }
+ }
+ *result = evaluateJSSourceValue(value, data->item->outerItem()).scriptValue;
+ }
+
+ struct JSSourceValueEvaluationResult
+ {
+ JSValue scriptValue = JS_UNDEFINED;
+ bool tryNextAlternative = true;
+ bool hasError = false;
+ };
+
+ JSSourceValueEvaluationResult evaluateJSSourceValue(const JSSourceValue *value, Item *outerItem,
+ const JSSourceValue::Alternative *alternative = nullptr,
+ JSSourceValue *elseCaseValue = nullptr, JSValue *outerScriptValue = nullptr)
+ {
+ JSSourceValueEvaluationResult result;
+ QBS_ASSERT(!alternative || value == alternative->value.get(), return result);
+ AutoScopePopper autoScopePopper(this);
+ auto maybeExtraScope = createExtraScope(value, outerItem, outerScriptValue);
+ if (!maybeExtraScope.second) {
+ result.scriptValue = maybeExtraScope.first;
+ result.hasError = true;
+ return result;
+ }
+ const ScopedJsValue extraScopeMgr(engine->context(), maybeExtraScope.first);
+ const Evaluator::FileContextScopes fileCtxScopes
+ = data->evaluator->fileContextScopes(value->file());
+ if (JsException ex = engine->checkAndClearException({})) {
+ result.scriptValue = engine->throwError(ex.toErrorInfo().toString());
+ result.hasError = true;
+ return result;
+ }
+ pushScope(fileCtxScopes.fileScope);
+ pushItemScopes(data->item);
+ if (itemOfProperty->type() != ItemType::ModuleInstance) {
+ // Own properties of module instances must not have the instance itself in the scope.
+ pushScope(*object);
+ }
+ if (value->definingItem())
+ pushItemScopes(value->definingItem());
+ pushScope(maybeExtraScope.first);
+ pushScope(fileCtxScopes.importScope);
+ if (alternative) {
+ ScopedJsValue sv(engine->context(), engine->evaluate(JsValueOwner::Caller,
+ alternative->condition.value, {}, 1, scopeChain));
+ if (JsException ex = engine->checkAndClearException(alternative->condition.location)) {
+ result.scriptValue = engine->throwError(ex.toErrorInfo().toString());
+ //result.scriptValue = JS_Throw(engine->context(), ex.takeValue());
+ //result.scriptValue = ex.takeValue();
+ result.hasError = true;
+ return result;
+ }
+ if (JS_ToBool(engine->context(), sv)) {
+ // The condition is true. Continue evaluating the value.
+ result.tryNextAlternative = false;
+ } else {
+ // The condition is false. Try the next alternative or the else value.
+ result.tryNextAlternative = true;
+ return result;
+ }
+ sv.reset(engine->evaluate(JsValueOwner::Caller,
+ alternative->overrideListProperties.value, {}, 1, scopeChain));
+ if (JsException ex = engine->checkAndClearException(
+ alternative->overrideListProperties.location)) {
+ result.scriptValue = engine->throwError(ex.toErrorInfo().toString());
+ //result.scriptValue = JS_Throw(engine->context(), ex.takeValue());
+ result.hasError = true;
+ return result;
+ }
+ if (JS_ToBool(engine->context(), sv))
+ elseCaseValue->setIsExclusiveListValue();
+ }
+ result.scriptValue = engine->evaluate(JsValueOwner::ScriptEngine,
+ value->sourceCodeForEvaluation(), value->file()->filePath(), value->line(),
+ scopeChain);
+ return result;
+ }
+
+ void handle(ItemValue *value) override
+ {
+ *result = data->evaluator->scriptValue(value->item());
+ if (JS_IsUninitialized(*result))
+ qDebug() << "SVConverter returned invalid script value.";
+ }
+
+ void handle(VariantValue *variantValue) override
+ {
+ *result = engine->toScriptValue(variantValue->value());
+ engine->takeOwnership(*result);
+ }
+};
+
+
+static QString resultToString(JSContext *ctx, const JSValue &scriptValue)
+{
+ if (JS_IsUndefined(scriptValue))
+ return QLatin1String("undefined");
+ if (JS_IsArray(ctx, scriptValue))
+ return getJsStringList(ctx, scriptValue).join(QLatin1Char(','));
+ if (JS_IsObject(scriptValue)) {
+ return QStringLiteral("[Object: ")
+ + QString::number(jsObjectId(scriptValue)) + QLatin1Char(']');
+ }
+ return getJsVariant(ctx, scriptValue).toString();
+}
+
+static void collectValuesFromNextChain(ScriptEngine *engine, const EvaluationData *data,
+ JSValue *result, const QString &propertyName,
+ const ValuePtr &value)
+{
+ JSValueList lst;
+ Set<Value *> &currentNextChain = data->evaluator->currentNextChain();
+ Set<Value *> oldNextChain = currentNextChain;
+ for (ValuePtr next = value; next; next = next->next())
+ currentNextChain.insert(next.get());
+
+ for (ValuePtr next = value; next; next = next->next()) {
+ ScopedJsValue v(engine->context(),
+ data->evaluator->property(next->definingItem(), propertyName));
+ if (JsException ex = engine->checkAndClearException({})) {
+ const ScopedJsValueList l(engine->context(), lst);
+ *result = engine->throwError(ex.toErrorInfo().toString());
+ return;
+ }
+ if (JS_IsUndefined(v))
+ continue;
+ lst.push_back(v.release());
+ if (next->type() == Value::JSSourceValueType
+ && std::static_pointer_cast<JSSourceValue>(next)->isExclusiveListValue()) {
+ // TODO: Why on earth do we keep the last _2_ elements?
+ auto keepIt = lst.rbegin();
+ for (int i = 0; i < 2 && keepIt != lst.rend(); ++i)
+ ++keepIt;
+ for (auto it = lst.begin(); it < keepIt.base(); ++it)
+ JS_FreeValue(engine->context(), *it);
+ lst.erase(lst.begin(), keepIt.base());
+ break;
+ }
+ }
+ currentNextChain = oldNextChain;
+
+ *result = engine->newArray(int(lst.size()), JsValueOwner::ScriptEngine);
+ quint32 k = 0;
+ JSContext * const ctx = engine->context();
+ for (const JSValue &v : qAsConst(lst)) {
+ QBS_ASSERT(!JS_IsError(ctx, v), continue);
+ if (JS_IsArray(ctx, v)) {
+ const quint32 vlen = getJsIntProperty(ctx, v, StringConstants::lengthProperty());
+ for (quint32 j = 0; j < vlen; ++j)
+ JS_SetPropertyUint32(ctx, *result, k++, JS_GetPropertyUint32(ctx, v, j));
+ JS_FreeValue(ctx, v);
+ } else {
+ JS_SetPropertyUint32(ctx, *result, k++, v);
+ }
+ }
+ setJsProperty(ctx, *result, StringConstants::lengthProperty(), JS_NewInt32(ctx, k));
+}
+
+static void convertToPropertyType(ScriptEngine *engine, const Item *item,
+ const PropertyDeclaration& decl, const Value *value, JSValue &v)
+{
+ if (value->type() == Value::VariantValueType && JS_IsUndefined(v) && !decl.isScalar()) {
+ v = engine->newArray(0, JsValueOwner::ScriptEngine); // QTBUG-51237
+ return;
+ }
+ convertToPropertyType_impl(engine, engine->evaluator()->pathPropertiesBaseDir(), item, decl,
+ value->location(), v);
+}
+
+struct EvalResult { JSValue v = JS_UNDEFINED; bool found = false; };
+static EvalResult getEvalProperty(ScriptEngine *engine, JSValue obj, const Item *item,
+ const QString &name, const EvaluationData *data)
+{
+ Evaluator * const evaluator = data->evaluator;
+ for (; item; item = item->prototype()) {
+ const ValuePtr value = item->ownProperty(name);
+ if (!value)
+ continue;
+ const Item * const itemOfProperty = item; // The item that owns the property.
+ PropertyStackManager propStackmanager(itemOfProperty, name, value.get(),
+ evaluator->requestedProperties(),
+ evaluator->propertyDependencies());
+ JSValue result;
+ if (evaluator->cachingEnabled()) {
+ const auto result = data->valueCache.constFind(name);
+ if (result != data->valueCache.constEnd()) {
+ if (debugProperties)
+ qDebug() << "[SC] cache hit " << name << ": "
+ << resultToString(engine->context(), *result);
+ return {*result, true};
+ }
+ }
+
+ if (value->next() && !evaluator->currentNextChain().contains(value.get())) {
+ collectValuesFromNextChain(engine, data, &result, name, value);
+ } else {
+ SVConverter converter(engine, &obj, value, itemOfProperty, &name, data, &result);
+ converter.start();
+ const PropertyDeclaration decl = data->item->propertyDeclaration(name);
+ convertToPropertyType(engine, data->item, decl, value.get(), result);
+ }
+
+ if (debugProperties)
+ qDebug() << "[SC] cache miss " << name << ": "
+ << resultToString(engine->context(), result);
+ if (evaluator->cachingEnabled()) {
+ const auto it = data->valueCache.find(name);
+ if (it != data->valueCache.end()) {
+ JS_FreeValue(engine->context(), it.value());
+ it.value() = JS_DupValue(engine->context(), result);
+ } else {
+ data->valueCache.insert(name, JS_DupValue(engine->context(), result));
+ }
+ }
+ return {result, true};
+ }
+ return {JS_UNDEFINED, false};
+}
+
+static int getEvalProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValue obj, JSAtom prop)
+{
+ if (desc) {
+ desc->getter = desc->setter = desc->value = JS_UNDEFINED;
+ desc->flags = JS_PROP_ENUMERABLE;
+ }
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ Evaluator * const evaluator = engine->evaluator();
+ const auto data = attachedPointer<EvaluationData>(obj, evaluator->classId());
+ const QString name = getJsString(ctx, prop);
+ if (debugProperties)
+ qDebug() << "[SC] queryProperty " << jsObjectId(obj) << " " << name;
+
+ if (name == QStringLiteral("parent")) {
+ if (desc) {
+ Item * const parent = data->item->parent();
+ desc->value = parent
+ ? JS_DupValue(ctx, data->evaluator->scriptValue(data->item->parent()))
+ : JS_UNDEFINED;
+ }
+ return 1;
+ }
+
+ if (!data) {
+ if (debugProperties)
+ qDebug() << "[SC] queryProperty: no data attached";
+ engine->setLastLookupStatus(false);
+ return -1;
+ }
+
+ EvalResult result = getEvalProperty(engine, obj, data->item, name, data);
+ if (!result.found && data->item->parent()) {
+ if (debugProperties)
+ qDebug() << "[SC] queryProperty: query parent";
+ const Item * const parentItem = data->item->parent();
+ result = getEvalProperty(engine, evaluator->scriptValue(parentItem), parentItem,
+ name, data);
+ }
+ if (result.found) {
+ if (desc)
+ desc->value = JS_DupValue(ctx, result.v);
+ engine->setLastLookupStatus(true);
+ return 1;
+ }
+
+ if (debugProperties)
+ qDebug() << "[SC] queryProperty: no such property";
+ engine->setLastLookupStatus(false);
+ return 0;
}
} // namespace Internal
diff --git a/src/lib/corelib/language/evaluator.h b/src/lib/corelib/language/evaluator.h
index 1e21391dc..f7aee8e46 100644
--- a/src/lib/corelib/language/evaluator.h
+++ b/src/lib/corelib/language/evaluator.h
@@ -44,16 +44,16 @@
#include "itemobserver.h"
#include "qualifiedid.h"
-#include <QtCore/qhash.h>
+#include <quickjs.h>
-#include <QtScript/qscriptvalue.h>
+#include <QtCore/qhash.h>
#include <functional>
#include <optional>
+#include <stack>
namespace qbs {
namespace Internal {
-class EvaluatorScriptClass;
class FileTags;
class Logger;
class PropertyDeclaration;
@@ -68,9 +68,10 @@ public:
~Evaluator() override;
ScriptEngine *engine() const { return m_scriptEngine; }
- QScriptValue property(const Item *item, const QString &name);
+ JSClassID classId() const { return m_scriptClass; }
+ JSValue property(const Item *item, const QString &name);
- QScriptValue value(const Item *item, const QString &name, bool *propertySet = nullptr);
+ JSValue value(const Item *item, const QString &name, bool *propertySet = nullptr);
bool boolValue(const Item *item, const QString &name, bool *propertyWasSet = nullptr);
int intValue(const Item *item, const QString &name, int defaultValue = 0,
bool *propertyWasSet = nullptr);
@@ -83,42 +84,51 @@ public:
bool *propertyWasSet = nullptr);
void convertToPropertyType(const PropertyDeclaration& decl, const CodeLocation &loc,
- QScriptValue &v);
+ JSValue &v);
- QScriptValue scriptValue(const Item *item);
+ JSValue scriptValue(const Item *item);
struct FileContextScopes
{
- QScriptValue fileScope;
- QScriptValue importScope;
+ JSValue fileScope = JS_UNDEFINED;
+ JSValue importScope = JS_UNDEFINED;
};
FileContextScopes fileContextScopes(const FileContextConstPtr &file);
- void setCachingEnabled(bool enabled);
+ void setCachingEnabled(bool enabled) { m_valueCacheEnabled = enabled; }
+ bool cachingEnabled() const { return m_valueCacheEnabled; }
+
+ PropertyDependencies &propertyDependencies() { return m_propertyDependencies; }
+ void clearPropertyDependencies() { m_propertyDependencies.clear(); }
- PropertyDependencies propertyDependencies() const;
- void clearPropertyDependencies();
+ std::stack<QualifiedId> &requestedProperties() { return m_requestedProperties; }
+ Set<Value *> &currentNextChain() { return m_currentNextChain; }
- void handleEvaluationError(const Item *item, const QString &name,
- const QScriptValue &scriptValue);
+ void handleEvaluationError(const Item *item, const QString &name);
- void setPathPropertiesBaseDir(const QString &dirPath);
- void clearPathPropertiesBaseDir();
+ QString pathPropertiesBaseDir() const { return m_pathPropertiesBaseDir; }
+ void setPathPropertiesBaseDir(const QString &dirPath) { m_pathPropertiesBaseDir = dirPath; }
+ void clearPathPropertiesBaseDir() { m_pathPropertiesBaseDir.clear(); }
bool isNonDefaultValue(const Item *item, const QString &name) const;
private:
void onItemPropertyChanged(Item *item) override;
- bool evaluateProperty(QScriptValue *result, const Item *item, const QString &name,
+ bool evaluateProperty(JSValue *result, const Item *item, const QString &name,
bool *propertyWasSet);
- ScriptEngine *m_scriptEngine;
- EvaluatorScriptClass *m_scriptClass;
- mutable QHash<const Item *, QScriptValue> m_scriptValueMap;
+ ScriptEngine * const m_scriptEngine;
+ const JSClassID m_scriptClass;
+ mutable QHash<const Item *, JSValue> m_scriptValueMap;
mutable QHash<FileContextConstPtr, FileContextScopes> m_fileContextScopesMap;
+ QString m_pathPropertiesBaseDir;
+ PropertyDependencies m_propertyDependencies;
+ std::stack<QualifiedId> m_requestedProperties;
+ Set<Value *> m_currentNextChain;
+ bool m_valueCacheEnabled = false;
};
-void throwOnEvaluationError(ScriptEngine *engine, const QScriptValue &scriptValue,
+void throwOnEvaluationError(ScriptEngine *engine,
const std::function<CodeLocation()> &provideFallbackCodeLocation);
class EvalCacheEnabler
diff --git a/src/lib/corelib/language/evaluatorscriptclass.cpp b/src/lib/corelib/language/evaluatorscriptclass.cpp
deleted file mode 100644
index 375954133..000000000
--- a/src/lib/corelib/language/evaluatorscriptclass.cpp
+++ /dev/null
@@ -1,776 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "evaluatorscriptclass.h"
-
-#include "evaluationdata.h"
-#include "evaluator.h"
-#include "filecontext.h"
-#include "item.h"
-#include "scriptengine.h"
-#include "propertydeclaration.h"
-#include "value.h"
-#include <logging/translator.h>
-#include <tools/fileinfo.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/scripttools.h>
-#include <tools/stringconstants.h>
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qsettings.h>
-
-#include <QtScript/qscriptclasspropertyiterator.h>
-#include <QtScript/qscriptstring.h>
-#include <QtScript/qscriptvalue.h>
-
-#include <utility>
-
-namespace qbs {
-namespace Internal {
-
-class SVConverter : ValueHandler
-{
- EvaluatorScriptClass * const scriptClass;
- ScriptEngine * const engine;
- QScriptContext * const scriptContext;
- const QScriptValue * const object;
- Value * const valuePtr;
- const Item * const itemOfProperty;
- const QScriptString * const propertyName;
- const EvaluationData * const data;
- QScriptValue * const result;
- char pushedScopesCount;
-
-public:
-
- SVConverter(EvaluatorScriptClass *esc, const QScriptValue *obj, const ValuePtr &v,
- const Item *_itemOfProperty, const QScriptString *propertyName, const EvaluationData *data,
- QScriptValue *result)
- : scriptClass(esc)
- , engine(static_cast<ScriptEngine *>(esc->engine()))
- , scriptContext(esc->engine()->currentContext())
- , object(obj)
- , valuePtr(v.get())
- , itemOfProperty(_itemOfProperty)
- , propertyName(propertyName)
- , data(data)
- , result(result)
- , pushedScopesCount(0)
- {
- }
-
- void start()
- {
- valuePtr->apply(this);
- }
-
-private:
- friend class AutoScopePopper;
-
- class AutoScopePopper
- {
- public:
- AutoScopePopper(SVConverter *converter)
- : m_converter(converter)
- {
- }
-
- ~AutoScopePopper()
- {
- m_converter->popScopes();
- }
-
- private:
- SVConverter *m_converter;
- };
-
- void setupConvenienceProperty(const QString &conveniencePropertyName, QScriptValue *extraScope,
- const QScriptValue &scriptValue)
- {
- if (!extraScope->isObject())
- *extraScope = engine->newObject();
- const PropertyDeclaration::Type type
- = itemOfProperty->propertyDeclaration(propertyName->toString()).type();
- const bool isArray = type == PropertyDeclaration::StringList
- || type == PropertyDeclaration::PathList
- || type == PropertyDeclaration::Variant // TODO: Why?
- || type == PropertyDeclaration::VariantList;
- QScriptValue valueToSet = scriptValue;
- if (isArray) {
- if (!valueToSet.isValid() || valueToSet.isUndefined())
- valueToSet = engine->newArray();
- } else if (!valueToSet.isValid()) {
- valueToSet = engine->undefinedValue();
- }
- extraScope->setProperty(conveniencePropertyName, valueToSet);
- }
-
- std::pair<QScriptValue, bool> createExtraScope(const JSSourceValue *value, Item *outerItem,
- QScriptValue *outerScriptValue)
- {
- std::pair<QScriptValue, bool> result;
- auto &extraScope = result.first;
- result.second = true;
- if (value->sourceUsesBase()) {
- QScriptValue baseValue;
- if (value->baseValue()) {
- SVConverter converter(scriptClass, object, value->baseValue(), itemOfProperty,
- propertyName, data, &baseValue);
- converter.start();
- }
- setupConvenienceProperty(StringConstants::baseVar(), &extraScope, baseValue);
- }
- if (value->sourceUsesOuter()) {
- QScriptValue v;
- if (outerItem) {
- v = data->evaluator->property(outerItem, *propertyName);
- if (engine->hasErrorOrException(v)) {
- extraScope = engine->lastErrorValue(v);
- result.second = false;
- return result;
- }
- } else if (outerScriptValue) {
- v = *outerScriptValue;
- }
- if (v.isValid())
- setupConvenienceProperty(StringConstants::outerVar(), &extraScope, v);
- }
- if (value->sourceUsesOriginal()) {
- QScriptValue originalValue;
- if (data->item->propertyDeclaration(propertyName->toString()).isScalar()) {
- const Item *item = itemOfProperty;
- if (item->type() == ItemType::Module || item->type() == ItemType::Export) {
- const QString errorMessage = Tr::tr("The special value 'original' cannot "
- "be used on the right-hand side of a property declaration.");
- extraScope = engine->currentContext()->throwError(errorMessage);
- result.second = false;
- return result;
- }
-
- // TODO: Provide a dedicated item type for not-yet-instantiated things that
- // look like module instances in the AST visitor.
- if (item->type() == ItemType::ModuleInstance
- && !item->hasProperty(StringConstants::presentProperty())) {
- const QString errorMessage = Tr::tr("Trying to assign property '%1' "
- "on something that is not a module.").arg(propertyName->toString());
- extraScope = engine->currentContext()->throwError(errorMessage);
- result.second = false;
- return result;
- }
-
- while (item->type() == ItemType::ModuleInstance)
- item = item->prototype();
- if (item->type() != ItemType::Module && item->type() != ItemType::Export) {
- const QString errorMessage = Tr::tr("The special value 'original' can only "
- "be used with module properties.");
- extraScope = engine->currentContext()->throwError(errorMessage);
- result.second = false;
- return result;
- }
- const ValuePtr v = item->property(*propertyName);
-
- // This can happen when resolving shadow products. The error will be ignored
- // in that case.
- if (!v) {
- const QString errorMessage = Tr::tr("Error setting up 'original'.");
- extraScope = engine->currentContext()->throwError(errorMessage);
- result.second = false;
- return result;
- }
-
- SVConverter converter(scriptClass, object, v, item,
- propertyName, data, &originalValue);
- converter.start();
- } else {
- originalValue = engine->newArray(0);
- }
- setupConvenienceProperty(StringConstants::originalVar(), &extraScope, originalValue);
- }
- return result;
- }
-
- void pushScope(const QScriptValue &scope)
- {
- if (scope.isObject()) {
- scriptContext->pushScope(scope);
- ++pushedScopesCount;
- }
- }
-
- void pushItemScopes(const Item *item)
- {
- const Item *scope = item->scope();
- if (scope) {
- pushItemScopes(scope);
- pushScope(data->evaluator->scriptValue(scope));
- }
- }
-
- void popScopes()
- {
- for (; pushedScopesCount; --pushedScopesCount)
- scriptContext->popScope();
- }
-
- void handle(JSSourceValue *value) override
- {
- QScriptValue outerScriptValue;
- for (const JSSourceValue::Alternative &alternative : value->alternatives()) {
- if (alternative.value->sourceUsesOuter()
- && !data->item->outerItem()
- && !outerScriptValue.isValid()) {
- JSSourceValueEvaluationResult sver = evaluateJSSourceValue(value, nullptr);
- if (sver.hasError) {
- *result = sver.scriptValue;
- return;
- }
- outerScriptValue = sver.scriptValue;
- }
- JSSourceValueEvaluationResult sver = evaluateJSSourceValue(alternative.value.get(),
- data->item->outerItem(),
- &alternative,
- value, &outerScriptValue);
- if (!sver.tryNextAlternative || sver.hasError) {
- *result = sver.scriptValue;
- return;
- }
- }
- *result = evaluateJSSourceValue(value, data->item->outerItem()).scriptValue;
- }
-
- struct JSSourceValueEvaluationResult
- {
- QScriptValue scriptValue;
- bool tryNextAlternative = true;
- bool hasError = false;
- };
-
- void injectErrorLocation(QScriptValue &sv, const CodeLocation &loc)
- {
- if (sv.isError() && !engine->lastErrorLocation(sv).isValid())
- sv = engine->currentContext()->throwError(engine->lastError(sv, loc).toString());
- }
-
- JSSourceValueEvaluationResult evaluateJSSourceValue(const JSSourceValue *value, Item *outerItem,
- const JSSourceValue::Alternative *alternative = nullptr,
- JSSourceValue *elseCaseValue = nullptr, QScriptValue *outerScriptValue = nullptr)
- {
- JSSourceValueEvaluationResult result;
- QBS_ASSERT(!alternative || value == alternative->value.get(), return result);
- AutoScopePopper autoScopePopper(this);
- auto maybeExtraScope = createExtraScope(value, outerItem, outerScriptValue);
- if (!maybeExtraScope.second) {
- result.scriptValue = maybeExtraScope.first;
- result.hasError = true;
- return result;
- }
- const Evaluator::FileContextScopes fileCtxScopes
- = data->evaluator->fileContextScopes(value->file());
- if (fileCtxScopes.importScope.isError()) {
- result.scriptValue = fileCtxScopes.importScope;
- result.hasError = true;
- return result;
- }
- pushScope(fileCtxScopes.fileScope);
- pushItemScopes(data->item);
- if (itemOfProperty->type() != ItemType::ModuleInstance) {
- // Own properties of module instances must not have the instance itself in the scope.
- pushScope(*object);
- }
- if (value->definingItem())
- pushItemScopes(value->definingItem());
- pushScope(maybeExtraScope.first);
- pushScope(fileCtxScopes.importScope);
- if (alternative) {
- QScriptValue sv = engine->evaluate(alternative->condition.value);
- if (engine->hasErrorOrException(sv)) {
- result.scriptValue = sv;
- result.hasError = true;
- injectErrorLocation(result.scriptValue, alternative->condition.location);
- return result;
- }
- if (sv.toBool()) {
- // The condition is true. Continue evaluating the value.
- result.tryNextAlternative = false;
- } else {
- // The condition is false. Try the next alternative or the else value.
- result.tryNextAlternative = true;
- return result;
- }
- sv = engine->evaluate(alternative->overrideListProperties.value);
- if (engine->hasErrorOrException(sv)) {
- result.scriptValue = sv;
- result.hasError = true;
- injectErrorLocation(result.scriptValue,
- alternative->overrideListProperties.location);
- return result;
- }
- if (sv.toBool())
- elseCaseValue->setIsExclusiveListValue();
- }
- result.scriptValue = engine->evaluate(value->sourceCodeForEvaluation(),
- value->file()->filePath(), value->line());
- return result;
- }
-
- void handle(ItemValue *value) override
- {
- *result = data->evaluator->scriptValue(value->item());
- if (!result->isValid())
- qDebug() << "SVConverter returned invalid script value.";
- }
-
- void handle(VariantValue *variantValue) override
- {
- *result = engine->toScriptValue(variantValue->value());
- }
-};
-
-bool debugProperties = false;
-
-enum QueryPropertyType
-{
- QPTDefault,
- QPTParentProperty
-};
-
-EvaluatorScriptClass::EvaluatorScriptClass(ScriptEngine *scriptEngine)
- : QScriptClass(scriptEngine)
- , m_valueCacheEnabled(false)
-{
-}
-
-QScriptClass::QueryFlags EvaluatorScriptClass::queryProperty(const QScriptValue &object,
- const QScriptString &name,
- QScriptClass::QueryFlags flags,
- uint *id)
-{
- Q_UNUSED(flags);
-
- // We assume that it's safe to save the result of the query in a member of the scriptclass.
- // It must be cleared in the property method before doing any further lookup.
- QBS_ASSERT(m_queryResult.isNull(), return {});
-
- if (debugProperties)
- qDebug() << "[SC] queryProperty " << object.objectId() << " " << name;
-
- auto const data = attachedPointer<EvaluationData>(object);
- const QString nameString = name.toString();
- if (nameString == QStringLiteral("parent")) {
- *id = QPTParentProperty;
- m_queryResult.data = data;
- return QScriptClass::HandlesReadAccess;
- }
-
- *id = QPTDefault;
- if (!data) {
- if (debugProperties)
- qDebug() << "[SC] queryProperty: no data attached";
- return {};
- }
-
- return queryItemProperty(data, nameString);
-}
-
-QScriptClass::QueryFlags EvaluatorScriptClass::queryItemProperty(const EvaluationData *data,
- const QString &name,
- bool ignoreParent)
-{
- for (const Item *item = data->item; item; item = item->prototype()) {
- m_queryResult.value = item->ownProperty(name);
- if (m_queryResult.value) {
- m_queryResult.data = data;
- m_queryResult.itemOfProperty = item;
- return HandlesReadAccess;
- }
- }
-
- if (!ignoreParent && data->item && data->item->parent()) {
- if (debugProperties)
- qDebug() << "[SC] queryProperty: query parent";
- EvaluationData parentdata = *data;
- parentdata.item = data->item->parent();
- const QueryFlags qf = queryItemProperty(&parentdata, name, true);
- if (qf.testFlag(HandlesReadAccess)) {
- m_queryResult.foundInParent = true;
- m_queryResult.data = data;
- return qf;
- }
- }
-
- if (debugProperties)
- qDebug() << "[SC] queryProperty: no such property";
- return {};
-}
-
-QString EvaluatorScriptClass::resultToString(const QScriptValue &scriptValue)
-{
- return (scriptValue.isObject()
- ? QStringLiteral("[Object: ")
- + QString::number(scriptValue.objectId()) + QLatin1Char(']')
- : scriptValue.toVariant().toString());
-}
-
-void EvaluatorScriptClass::collectValuesFromNextChain(const EvaluationData *data, QScriptValue *result,
- const QString &propertyName, const ValuePtr &value)
-{
- QScriptValueList lst;
- Set<Value *> oldNextChain = m_currentNextChain;
- for (ValuePtr next = value; next; next = next->next())
- m_currentNextChain.insert(next.get());
-
- for (ValuePtr next = value; next; next = next->next()) {
- QScriptValue v = data->evaluator->property(next->definingItem(), propertyName);
- const auto se = static_cast<const ScriptEngine *>(engine());
- if (se->hasErrorOrException(v)) {
- *result = se->lastErrorValue(v);
- return;
- }
- if (v.isUndefined())
- continue;
- lst << v;
- if (next->type() == Value::JSSourceValueType
- && std::static_pointer_cast<JSSourceValue>(next)->isExclusiveListValue()) {
- lst = lst.mid(lst.length() - 2);
- break;
- }
- }
- m_currentNextChain = oldNextChain;
-
- *result = engine()->newArray();
- quint32 k = 0;
- for (const QScriptValue &v : qAsConst(lst)) {
- QBS_ASSERT(!v.isError(), continue);
- if (v.isArray()) {
- const quint32 vlen = v.property(StringConstants::lengthProperty()).toInt32();
- for (quint32 j = 0; j < vlen; ++j)
- result->setProperty(k++, v.property(j));
- } else {
- result->setProperty(k++, v);
- }
- }
-}
-
-static QString overriddenSourceDirectory(const Item *item, const QString &defaultValue)
-{
- const VariantValuePtr v = item->variantProperty
- (StringConstants::qbsSourceDirPropertyInternal());
- return v ? v->value().toString() : defaultValue;
-}
-
-static void makeTypeError(const ErrorInfo &error, QScriptValue &v)
-{
- v = v.engine()->currentContext()->throwError(QScriptContext::TypeError,
- error.toString());
-}
-
-static void makeTypeError(const PropertyDeclaration &decl, const CodeLocation &location,
- QScriptValue &v)
-{
- const ErrorInfo error(Tr::tr("Value assigned to property '%1' does not have type '%2'.")
- .arg(decl.name(), decl.typeString()), location);
- makeTypeError(error, v);
-}
-
-static void convertToPropertyType_impl(const QString &pathPropertiesBaseDir, const Item *item,
- const PropertyDeclaration& decl,
- const CodeLocation &location, QScriptValue &v)
-{
- if (v.isUndefined() || v.isError())
- return;
- QString srcDir;
- QString actualBaseDir;
- if (item && !pathPropertiesBaseDir.isEmpty()) {
- const VariantValueConstPtr itemSourceDir
- = item->variantProperty(QStringLiteral("sourceDirectory"));
- actualBaseDir = itemSourceDir ? itemSourceDir->value().toString() : pathPropertiesBaseDir;
- }
- switch (decl.type()) {
- case PropertyDeclaration::UnknownType:
- case PropertyDeclaration::Variant:
- break;
- case PropertyDeclaration::Boolean:
- if (!v.isBool())
- v = v.toBool();
- break;
- case PropertyDeclaration::Integer:
- if (!v.isNumber())
- makeTypeError(decl, location, v);
- break;
- case PropertyDeclaration::Path:
- {
- if (!v.isString()) {
- makeTypeError(decl, location, v);
- break;
- }
- const QString srcDir = item ? overriddenSourceDirectory(item, actualBaseDir)
- : pathPropertiesBaseDir;
- if (!srcDir.isEmpty())
- v = v.engine()->toScriptValue(QDir::cleanPath(
- FileInfo::resolvePath(srcDir, v.toString())));
- break;
- }
- case PropertyDeclaration::String:
- if (!v.isString())
- makeTypeError(decl, location, v);
- break;
- case PropertyDeclaration::PathList:
- srcDir = item ? overriddenSourceDirectory(item, actualBaseDir)
- : pathPropertiesBaseDir;
- // Fall-through.
- case PropertyDeclaration::StringList:
- {
- if (!v.isArray()) {
- QScriptValue x = v.engine()->newArray(1);
- x.setProperty(0, v);
- v = x;
- }
- const quint32 c = v.property(StringConstants::lengthProperty()).toUInt32();
- for (quint32 i = 0; i < c; ++i) {
- QScriptValue elem = v.property(i);
- if (elem.isUndefined()) {
- ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' is undefined. "
- "String expected.").arg(i).arg(decl.name()), location);
- makeTypeError(error, v);
- break;
- }
- if (elem.isNull()) {
- ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' is null. "
- "String expected.").arg(i).arg(decl.name()), location);
- makeTypeError(error, v);
- break;
- }
- if (!elem.isString()) {
- ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' does not have "
- "string type.").arg(i).arg(decl.name()), location);
- makeTypeError(error, v);
- break;
- }
- if (srcDir.isEmpty())
- continue;
- elem = v.engine()->toScriptValue(
- QDir::cleanPath(FileInfo::resolvePath(srcDir, elem.toString())));
- v.setProperty(i, elem);
- }
- break;
- }
- case PropertyDeclaration::VariantList:
- if (!v.isArray()) {
- QScriptValue x = v.engine()->newArray(1);
- x.setProperty(0, v);
- v = x;
- }
- break;
- }
-}
-
-void EvaluatorScriptClass::convertToPropertyType(const PropertyDeclaration &decl,
- const CodeLocation &loc, QScriptValue &v)
-{
- convertToPropertyType_impl(QString(), nullptr, decl, loc, v);
-}
-
-void EvaluatorScriptClass::convertToPropertyType(const Item *item, const PropertyDeclaration& decl,
- const Value *value, QScriptValue &v)
-{
- if (value->type() == Value::VariantValueType && v.isUndefined() && !decl.isScalar()) {
- v = v.engine()->newArray(); // QTBUG-51237
- return;
- }
- convertToPropertyType_impl(m_pathPropertiesBaseDir, item, decl, value->location(), v);
-}
-
-class PropertyStackManager
-{
-public:
- PropertyStackManager(const Item *itemOfProperty, const QScriptString &name, const Value *value,
- std::stack<QualifiedId> &requestedProperties,
- PropertyDependencies &propertyDependencies)
- : m_requestedProperties(requestedProperties)
- {
- if (value->type() == Value::JSSourceValueType
- && (itemOfProperty->type() == ItemType::ModuleInstance
- || itemOfProperty->type() == ItemType::Module
- || itemOfProperty->type() == ItemType::Export)) {
- const VariantValueConstPtr varValue
- = itemOfProperty->variantProperty(StringConstants::nameProperty());
- if (!varValue)
- return;
- m_stackUpdate = true;
- const QualifiedId fullPropName
- = QualifiedId::fromString(varValue->value().toString()) << name.toString();
- if (!requestedProperties.empty())
- propertyDependencies[fullPropName].insert(requestedProperties.top());
- m_requestedProperties.push(fullPropName);
- }
- }
-
- ~PropertyStackManager()
- {
- if (m_stackUpdate)
- m_requestedProperties.pop();
- }
-
-private:
- std::stack<QualifiedId> &m_requestedProperties;
- bool m_stackUpdate = false;
-};
-
-QScriptValue EvaluatorScriptClass::property(const QScriptValue &object, const QScriptString &name,
- uint id)
-{
- const bool foundInParent = m_queryResult.foundInParent;
- const EvaluationData *data = m_queryResult.data;
- const Item * const itemOfProperty = m_queryResult.itemOfProperty;
- m_queryResult.foundInParent = false;
- m_queryResult.data = nullptr;
- m_queryResult.itemOfProperty = nullptr;
- QBS_ASSERT(data, {});
-
- const auto qpt = static_cast<QueryPropertyType>(id);
- if (qpt == QPTParentProperty) {
- return data->item->parent()
- ? data->evaluator->scriptValue(data->item->parent())
- : engine()->undefinedValue();
- }
-
- ValuePtr value;
- m_queryResult.value.swap(value);
- QBS_ASSERT(value, return {});
- QBS_ASSERT(m_queryResult.isNull(), return {});
-
- if (debugProperties)
- qDebug() << "[SC] property " << name;
-
- PropertyStackManager propStackmanager(itemOfProperty, name, value.get(),
- m_requestedProperties, m_propertyDependencies);
-
- QScriptValue result;
- if (m_valueCacheEnabled) {
- result = data->valueCache.value(name);
- if (result.isValid()) {
- if (debugProperties)
- qDebug() << "[SC] cache hit " << name << ": " << resultToString(result);
- return result;
- }
- }
-
- if (value->next() && !m_currentNextChain.contains(value.get())) {
- collectValuesFromNextChain(data, &result, name.toString(), value);
- } else {
- QScriptValue parentObject;
- if (foundInParent)
- parentObject = data->evaluator->scriptValue(data->item->parent());
- SVConverter converter(this, foundInParent ? &parentObject : &object, value, itemOfProperty,
- &name, data, &result);
- converter.start();
-
- const PropertyDeclaration decl = data->item->propertyDeclaration(name.toString());
- convertToPropertyType(data->item, decl, value.get(), result);
- }
-
- if (debugProperties)
- qDebug() << "[SC] cache miss " << name << ": " << resultToString(result);
- if (m_valueCacheEnabled)
- data->valueCache.insert(name, result);
- return result;
-}
-
-class EvaluatorScriptClassPropertyIterator : public QScriptClassPropertyIterator
-{
-public:
- EvaluatorScriptClassPropertyIterator(const QScriptValue &object, EvaluationData *data)
- : QScriptClassPropertyIterator(object), m_it(data->item->properties())
- {
- }
-
- bool hasNext() const override
- {
- return m_it.hasNext();
- }
-
- void next() override
- {
- m_it.next();
- }
-
- bool hasPrevious() const override
- {
- return m_it.hasPrevious();
- }
-
- void previous() override
- {
- m_it.previous();
- }
-
- void toFront() override
- {
- m_it.toFront();
- }
-
- void toBack() override
- {
- m_it.toBack();
- }
-
- QScriptString name() const override
- {
- return object().engine()->toStringHandle(m_it.key());
- }
-
-private:
- QMapIterator<QString, ValuePtr> m_it;
-};
-
-QScriptClassPropertyIterator *EvaluatorScriptClass::newIterator(const QScriptValue &object)
-{
- auto const data = attachedPointer<EvaluationData>(object);
- return data ? new EvaluatorScriptClassPropertyIterator(object, data) : nullptr;
-}
-
-void EvaluatorScriptClass::setValueCacheEnabled(bool enabled)
-{
- m_valueCacheEnabled = enabled;
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/evaluatorscriptclass.h b/src/lib/corelib/language/evaluatorscriptclass.h
deleted file mode 100755
index c234c17fa..000000000
--- a/src/lib/corelib/language/evaluatorscriptclass.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QBS_EVALUATORSCRIPTCLASS_H
-#define QBS_EVALUATORSCRIPTCLASS_H
-
-#include "forward_decls.h"
-#include "qualifiedid.h"
-
-#include <tools/set.h>
-
-#include <QtScript/qscriptclass.h>
-
-#include <stack>
-
-QT_BEGIN_NAMESPACE
-class QScriptContext;
-QT_END_NAMESPACE
-
-namespace qbs {
-namespace Internal {
-class EvaluationData;
-class Item;
-class PropertyDeclaration;
-class ScriptEngine;
-
-class EvaluatorScriptClass : public QScriptClass
-{
-public:
- EvaluatorScriptClass(ScriptEngine *scriptEngine);
-
- QueryFlags queryProperty(const QScriptValue &object,
- const QScriptString &name,
- QueryFlags flags, uint *id) override;
- QScriptValue property(const QScriptValue &object,
- const QScriptString &name, uint id) override;
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override;
-
- void setValueCacheEnabled(bool enabled);
-
- void convertToPropertyType(const PropertyDeclaration& decl, const CodeLocation &loc,
- QScriptValue &v);
-
- PropertyDependencies propertyDependencies() const { return m_propertyDependencies; }
- void clearPropertyDependencies() { m_propertyDependencies.clear(); }
-
- void setPathPropertiesBaseDir(const QString &dirPath) { m_pathPropertiesBaseDir = dirPath; }
- void clearPathPropertiesBaseDir() { m_pathPropertiesBaseDir.clear(); }
-
-private:
- QueryFlags queryItemProperty(const EvaluationData *data,
- const QString &name,
- bool ignoreParent = false);
- static QString resultToString(const QScriptValue &scriptValue);
- void collectValuesFromNextChain(const EvaluationData *data, QScriptValue *result, const QString &propertyName, const ValuePtr &value);
-
- void convertToPropertyType(const Item *item,
- const PropertyDeclaration& decl, const Value *value,
- QScriptValue &v);
-
- struct QueryResult
- {
- QueryResult()
- : data(nullptr), itemOfProperty(nullptr)
- {}
-
- bool isNull() const
- {
- static const QueryResult pristine;
- return *this == pristine;
- }
-
- bool operator==(const QueryResult &rhs) const
- {
- return foundInParent == rhs.foundInParent
- && data == rhs.data
- && itemOfProperty == rhs.itemOfProperty
- && value == rhs.value;
- }
-
- bool foundInParent = false;
- const EvaluationData *data;
- const Item *itemOfProperty; // The item that owns the property.
- ValuePtr value;
- };
- QueryResult m_queryResult;
- bool m_valueCacheEnabled;
- Set<Value *> m_currentNextChain;
- PropertyDependencies m_propertyDependencies;
- std::stack<QualifiedId> m_requestedProperties;
- QString m_pathPropertiesBaseDir;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_EVALUATORSCRIPTCLASS_H
diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp
index bd134040d..3c6fcf454 100644
--- a/src/lib/corelib/language/language.cpp
+++ b/src/lib/corelib/language/language.cpp
@@ -68,8 +68,6 @@
#include <QtCore/qdiriterator.h>
#include <QtCore/qmap.h>
-#include <QtScript/qscriptvalue.h>
-
#include <algorithm>
#include <memory>
#include <mutex>
diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h
index 146f00f89..d055aa132 100644
--- a/src/lib/corelib/language/language.h
+++ b/src/lib/corelib/language/language.h
@@ -64,7 +64,7 @@
#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptvalue.h>
+#include <quickjs.h>
#include <memory>
#include <mutex>
@@ -335,7 +335,7 @@ class PrivateScriptFunction
friend bool operator==(const PrivateScriptFunction &a, const PrivateScriptFunction &b);
public:
void initialize(const ScriptFunctionPtr &sharedData) { m_sharedData = sharedData; }
- mutable QScriptValue scriptFunction; // not stored
+ mutable JSValue scriptFunction = JS_UNDEFINED; // not stored
QString &sourceCode() const { return m_sharedData->sourceCode; }
CodeLocation &location() const { return m_sharedData->location; }
diff --git a/src/lib/corelib/language/loader.cpp b/src/lib/corelib/language/loader.cpp
index 3fff71c10..1156b138b 100644
--- a/src/lib/corelib/language/loader.cpp
+++ b/src/lib/corelib/language/loader.cpp
@@ -56,7 +56,6 @@
#include <QtCore/qdir.h>
#include <QtCore/qobject.h>
-#include <QtCore/qtimer.h>
namespace qbs {
namespace Internal {
@@ -131,15 +130,13 @@ TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters &_parameters
<< QDir::toNativeSeparators(parameters.projectFilePath()) << "'.";
m_engine->setEnvironment(parameters.adjustedEnvironment());
- m_engine->clearExceptions();
+ m_engine->checkAndClearException({});
m_engine->clearImportsCache();
m_engine->clearRequestedProperties();
m_engine->enableProfiling(parameters.logElapsedTime());
m_logger.clearWarnings();
EvalContextSwitcher evalContextSwitcher(m_engine, EvalContext::PropertyEvaluation);
- QTimer cancelationTimer;
-
// At this point, we cannot set a sensible total effort, because we know nothing about
// the project yet. That's why we use a placeholder here, so the user at least
// sees that an operation is starting. The real total effort will be set later when
@@ -147,13 +144,7 @@ TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters &_parameters
if (m_progressObserver) {
m_progressObserver->initialize(Tr::tr("Resolving project for configuration %1")
.arg(TopLevelProject::deriveId(parameters.finalBuildConfigurationTree())), 1);
- cancelationTimer.setSingleShot(false);
- QObject::connect(&cancelationTimer, &QTimer::timeout, [this]() {
- QBS_ASSERT(m_progressObserver, return);
- if (m_progressObserver->canceled())
- m_engine->cancel();
- });
- cancelationTimer.start(1000);
+ m_progressObserver->setScriptEngine(m_engine);
}
const FileTime resolveTime = FileTime::currentTime();
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
index 86553916b..1663fccac 100644
--- a/src/lib/corelib/language/moduleloader.cpp
+++ b/src/lib/corelib/language/moduleloader.cpp
@@ -78,7 +78,6 @@
#include <QtCore/qjsonobject.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qthreadstorage.h>
-#include <QtScript/qscriptvalueiterator.h>
#include <algorithm>
#include <memory>
@@ -816,7 +815,8 @@ ModuleLoader::MultiplexInfo ModuleLoader::extractMultiplexInfo(Item *productItem
{
static const QString mpmKey = QStringLiteral("multiplexMap");
- const QScriptValue multiplexMap = m_evaluator->value(qbsModuleItem, mpmKey);
+ JSContext * const ctx = m_evaluator->engine()->context();
+ const ScopedJsValue multiplexMap(ctx, m_evaluator->value(qbsModuleItem, mpmKey));
const QStringList multiplexByQbsProperties = m_evaluator->stringListValue(
productItem, StringConstants::multiplexByQbsPropertiesProperty());
@@ -831,7 +831,7 @@ ModuleLoader::MultiplexInfo ModuleLoader::extractMultiplexInfo(Item *productItem
Set<QString> uniqueMultiplexByQbsProperties;
for (const QString &key : multiplexByQbsProperties) {
- const QString mappedKey = multiplexMap.property(key).toString();
+ const QString mappedKey = getJsStringProperty(ctx, multiplexMap, key);
if (mappedKey.isEmpty())
throw ErrorInfo(Tr::tr("There is no entry for '%1' in 'qbs.multiplexMap'.").arg(key));
@@ -841,13 +841,13 @@ ModuleLoader::MultiplexInfo ModuleLoader::extractMultiplexInfo(Item *productItem
productItem->location());
}
- const QScriptValue arr = m_evaluator->value(qbsModuleItem, key);
- if (arr.isUndefined())
+ const ScopedJsValue arr(ctx, m_evaluator->value(qbsModuleItem, key));
+ if (JS_IsUndefined(arr))
continue;
- if (!arr.isArray())
+ if (!JS_IsArray(ctx, arr))
throw ErrorInfo(Tr::tr("Property '%1' must be an array.").arg(key));
- const quint32 arrlen = arr.property(StringConstants::lengthProperty()).toUInt32();
+ const quint32 arrlen = getJsIntProperty(ctx, arr, StringConstants::lengthProperty());
if (arrlen == 0)
continue;
@@ -855,7 +855,8 @@ ModuleLoader::MultiplexInfo ModuleLoader::extractMultiplexInfo(Item *productItem
mprow.resize(arrlen);
QVariantList entriesForKey;
for (quint32 i = 0; i < arrlen; ++i) {
- const QVariant value = arr.property(i).toVariant();
+ const ScopedJsValue sv(ctx, JS_GetPropertyUint32(ctx, arr, i));
+ const QVariant value = getJsVariant(ctx, sv);
if (entriesForKey.contains(value)) {
throw ErrorInfo(Tr::tr("Duplicate entry '%1' in qbs.%2.")
.arg(value.toString(), key), productItem->location());
@@ -1922,8 +1923,8 @@ bool ModuleLoader::mergeExportItems(const ProductContext &productContext)
for (Item * const child : exportItem->children()) {
if (child->type() == ItemType::Parameters) {
adjustParametersScopes(child, child);
- mergeParameters(pmi.defaultParameters,
- m_evaluator->scriptValue(child).toVariant().toMap());
+ mergeParameters(pmi.defaultParameters, getJsVariant(
+ m_evaluator->engine()->context(), m_evaluator->scriptValue(child)).toMap());
} else {
if (child->type() == ItemType::Depends) {
bool productTypesIsSet;
@@ -2036,8 +2037,10 @@ void ModuleLoader::evaluateProfileValues(const QualifiedId &namePrefix, Item *it
item->setType(ItemType::ModulePrefix); // TODO: Introduce new item type
if (item != profileItem)
item->setScope(profileItem);
+ const ScopedJsValue sv(m_evaluator->engine()->context(),
+ m_evaluator->value(item, it.key()));
values.insert(name.join(QLatin1Char('.')),
- m_evaluator->value(item, it.key()).toVariant());
+ getJsVariant(m_evaluator->engine()->context(), sv));
break;
}
}
@@ -2710,18 +2713,17 @@ static Item::PropertyMap filterItemProperties(const Item::PropertyMap &propertie
return result;
}
-static QVariantMap safeToVariant(const QScriptValue &v)
+static QVariantMap safeToVariant(JSContext *ctx, const JSValue &v)
{
QVariantMap result;
- QScriptValueIterator it(v);
- while (it.hasNext()) {
- it.next();
- QScriptValue u = it.value();
- if (u.isError())
- throw ErrorInfo(u.toString());
- result[it.name()] = (u.isObject() && !u.isArray() && !u.isRegExp())
- ? safeToVariant(u) : it.value().toVariant();
- }
+ handleJsProperties(ctx, v, [&](const JSAtom &prop, const JSPropertyDescriptor &desc) {
+ const JSValue u = desc.value;
+ if (JS_IsError(ctx, u))
+ throw ErrorInfo(getJsString(ctx, u));
+ const QString name = getJsString(ctx, prop);
+ result[name] = (JS_IsObject(u) && !JS_IsArray(ctx, u) && !JS_IsRegExp(ctx, u))
+ ? safeToVariant(ctx, u) : getJsVariant(ctx, u);
+ });
return result;
}
@@ -2735,9 +2737,9 @@ QVariantMap ModuleLoader::extractParameters(Item *dependsItem) const
auto origProperties = dependsItem->properties();
dependsItem->setProperties(itemProperties);
- QScriptValue sv = m_evaluator->scriptValue(dependsItem);
+ JSValue sv = m_evaluator->scriptValue(dependsItem);
try {
- result = safeToVariant(sv);
+ result = safeToVariant(m_evaluator->engine()->context(), sv);
} catch (const ErrorInfo &exception) {
auto ei = exception;
ei.prepend(Tr::tr("Error in dependency parameter."), dependsItem->location());
@@ -3123,7 +3125,7 @@ std::pair<Item *, bool> ModuleLoader::loadModuleFile(
return {it.value() ? module : nullptr, triedToLoad};
}
- // Set the name before evaluating any properties. EvaluatorScriptClass reads the module name.
+ // Set the name before evaluating any properties. Evaluator reads the module name.
module->setProperty(StringConstants::nameProperty(), VariantValue::create(fullModuleName));
Item *deepestModuleInstance = findDeepestModuleInstance(moduleInstance);
diff --git a/src/lib/corelib/language/moduleproviderloader.cpp b/src/lib/corelib/language/moduleproviderloader.cpp
index 3e62d9ed9..dae8bba15 100644
--- a/src/lib/corelib/language/moduleproviderloader.cpp
+++ b/src/lib/corelib/language/moduleproviderloader.cpp
@@ -54,6 +54,7 @@
#include <tools/fileinfo.h>
#include <tools/jsliterals.h>
+#include <tools/scripttools.h>
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
@@ -182,9 +183,12 @@ QVariantMap ModuleProviderLoader::getModuleProviderConfig(
collectMap(childItem, QualifiedId(name) << it.key());
continue;
}
- case Value::JSSourceValueType:
- value = m_evaluator->value(item, it.key()).toVariant();
+ case Value::JSSourceValueType: {
+ const ScopedJsValue sv(m_evaluator->engine()->context(),
+ m_evaluator->value(item, it.key()));
+ value = getJsVariant(m_evaluator->engine()->context(), sv);
break;
+ }
case Value::VariantValueType:
value = static_cast<VariantValue *>(it.value().get())->value();
break;
@@ -270,7 +274,9 @@ QVariantMap ModuleProviderLoader::evaluateQbsModule(ProductContext &product) con
product.item->property(StringConstants::qbsModule()));
QVariantMap result;
for (const auto &property : properties) {
- auto value = m_evaluator->value(qbsItemValue->item(), property).toVariant();
+ const ScopedJsValue val(m_evaluator->engine()->context(),
+ m_evaluator->value(qbsItemValue->item(), property));
+ auto value = getJsVariant(m_evaluator->engine()->context(), val);
if (value.isValid())
result[property] = std::move(value);
}
diff --git a/src/lib/corelib/language/preparescriptobserver.cpp b/src/lib/corelib/language/preparescriptobserver.cpp
index 632cbfb51..11536e74d 100644
--- a/src/lib/corelib/language/preparescriptobserver.cpp
+++ b/src/lib/corelib/language/preparescriptobserver.cpp
@@ -48,8 +48,6 @@
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
@@ -58,22 +56,23 @@ PrepareScriptObserver::PrepareScriptObserver(ScriptEngine *engine, UnobserveMode
{
}
-void PrepareScriptObserver::onPropertyRead(const QScriptValue &object, const QString &name,
- const QScriptValue &value)
+void PrepareScriptObserver::onPropertyRead(const JSValue &object, const QString &name,
+ const JSValue &value)
{
- const auto objectId = object.objectId();
+ JSContext * const ctx = engine()->context();
+ const auto objectId = jsObjectId(object);
const auto projectIt = m_projectObjectIds.find(objectId);
if (projectIt != m_projectObjectIds.cend()) {
engine()->addPropertyRequestedInScript(
- Property(projectIt->second, QString(), name, value.toVariant(),
+ Property(projectIt->second, QString(), name, getJsVariant(ctx, value),
Property::PropertyInProject));
return;
}
if (m_importIds.contains(objectId)) {
- engine()->addImportRequestedInScript(object.objectId());
+ engine()->addImportRequestedInScript(jsObjectId(object));
return;
}
- const auto exportsIt = m_exportsObjectIds.find(value.objectId());
+ const auto exportsIt = m_exportsObjectIds.find(jsObjectId(object));
if (exportsIt != m_exportsObjectIds.cend()) {
engine()->addRequestedExport(exportsIt->second);
return;
@@ -81,13 +80,14 @@ void PrepareScriptObserver::onPropertyRead(const QScriptValue &object, const QSt
const auto it = m_parameterObjects.find(objectId);
if (it != m_parameterObjects.cend()) {
engine()->addPropertyRequestedInScript(
- Property(it->second.first, it->second.second, name, value.toVariant(),
+ Property(it->second.first, it->second.second, name, getJsVariant(ctx, value),
Property::PropertyInParameters));
}
if (name == StringConstants::fileTagsProperty() && m_artifactIds.contains(objectId)) {
- const Artifact * const artifact = attachedPointer<Artifact>(object);
+ const Artifact * const artifact = attachedPointer<Artifact>(object,
+ engine()->dataWithPtrClass());
QBS_CHECK(artifact);
- const Property p(artifact->product->uniqueName(), QString(), name, value.toVariant(),
+ const Property p(artifact->product->uniqueName(), QString(), name, getJsVariant(ctx, value),
Property::PropertyInArtifact);
engine()->addPropertyRequestedFromArtifact(artifact, p);
}
diff --git a/src/lib/corelib/language/preparescriptobserver.h b/src/lib/corelib/language/preparescriptobserver.h
index 36e395efc..5dc54cbb6 100644
--- a/src/lib/corelib/language/preparescriptobserver.h
+++ b/src/lib/corelib/language/preparescriptobserver.h
@@ -44,6 +44,8 @@
#include <tools/set.h>
+#include <quickjs.h>
+
#include <QtCore/qstring.h>
#include <unordered_map>
@@ -69,7 +71,7 @@ public:
}
void addArtifactId(qint64 artifactId) { m_artifactIds.insert(artifactId); }
- bool addImportId(qint64 importId) { return m_importIds.insert(importId).second; }
+ bool addImportId(quintptr importId) { return m_importIds.insert(importId).second; }
void clearImportIds() { m_importIds.clear(); }
void addParameterObjectId(qint64 id, const QString &productName, const QString &depName,
const QualifiedId &moduleName)
@@ -79,14 +81,13 @@ public:
m_parameterObjects.insert(std::make_pair(id, value));
}
-private:
- void onPropertyRead(const QScriptValue &object, const QString &name,
- const QScriptValue &value) override;
+ void onPropertyRead(const JSValue &object, const QString &name, const JSValue &value) override;
+private:
std::unordered_map<qint64, QString> m_projectObjectIds;
std::unordered_map<qint64, std::pair<QString, QString>> m_parameterObjects;
std::unordered_map<qint64, const ResolvedProduct *> m_exportsObjectIds;
- Set<qint64> m_importIds;
+ Set<quintptr> m_importIds;
Set<qint64> m_artifactIds;
};
diff --git a/src/lib/corelib/language/probesresolver.cpp b/src/lib/corelib/language/probesresolver.cpp
index 059080155..d2f3fefa2 100644
--- a/src/lib/corelib/language/probesresolver.cpp
+++ b/src/lib/corelib/language/probesresolver.cpp
@@ -57,8 +57,11 @@
#include <logging/logger.h>
#include <logging/translator.h>
#include <tools/profiling.h>
+#include <tools/scripttools.h>
#include <tools/stringconstants.h>
+#include <quickjs.h>
+
namespace qbs {
namespace Internal {
@@ -128,8 +131,10 @@ void ProbesResolver::resolveProbe(ModuleLoader::ProductContext *productContext,
QBS_CHECK(configureScript);
if (Q_UNLIKELY(configureScript->sourceCode() == StringConstants::undefinedValue()))
throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location());
- using ProbeProperty = std::pair<QString, QScriptValue>;
+ using ProbeProperty = std::pair<QString, ScopedJsValue>;
std::vector<ProbeProperty> probeBindings;
+ ScriptEngine * const engine = m_evaluator->engine();
+ JSContext * const ctx = engine->context();
QVariantMap initialProperties;
for (Item *obj = probe; obj; obj = obj->prototype()) {
const Item::PropertyMap &props = obj->properties();
@@ -137,14 +142,12 @@ void ProbesResolver::resolveProbe(ModuleLoader::ProductContext *productContext,
const QString &name = it.key();
if (name == StringConstants::configureProperty())
continue;
- const QScriptValue value = m_evaluator->value(probe, name);
- probeBindings << ProbeProperty(name, value);
+ const JSValue value = m_evaluator->value(probe, name);
+ probeBindings.emplace_back(name, ScopedJsValue(ctx, value));
if (name != StringConstants::conditionProperty())
- initialProperties.insert(name, value.toVariant());
+ initialProperties.insert(name, getJsVariant(ctx, value));
}
}
- ScriptEngine * const engine = m_evaluator->engine();
- QScriptValue configureScope;
const bool condition = m_evaluator->boolValue(probe, StringConstants::conditionProperty());
const QString &sourceCode = configureScript->sourceCode().toString();
ProbeConstPtr resolvedProbe;
@@ -166,6 +169,7 @@ void ProbesResolver::resolveProbe(ModuleLoader::ProductContext *productContext,
qCDebug(lcModuleLoader) << "probe results cached from earlier run";
++m_probesCachedOld;
}
+ ScopedJsValue configureScope(ctx, JS_UNDEFINED);
std::vector<QString> importedFilesUsedInConfigure;
if (!condition) {
qCDebug(lcModuleLoader) << "Probe disabled; skipping";
@@ -174,20 +178,15 @@ void ProbesResolver::resolveProbe(ModuleLoader::ProductContext *productContext,
qCDebug(lcModuleLoader) << "configure script needs to run";
const Evaluator::FileContextScopes fileCtxScopes
= m_evaluator->fileContextScopes(configureScript->file());
- engine->currentContext()->pushScope(fileCtxScopes.fileScope);
- engine->currentContext()->pushScope(fileCtxScopes.importScope);
- configureScope = engine->newObject();
+ configureScope.setValue(engine->newObject());
for (const ProbeProperty &b : probeBindings)
- configureScope.setProperty(b.first, b.second);
- engine->currentContext()->pushScope(configureScope);
+ setJsProperty(ctx, configureScope, b.first, JS_DupValue(ctx, b.second));
engine->clearRequestedProperties();
- QScriptValue sv = engine->evaluate(configureScript->sourceCodeForEvaluation());
- engine->currentContext()->popScope();
- engine->currentContext()->popScope();
- engine->currentContext()->popScope();
- engine->releaseResourcesOfScriptObjects();
- if (Q_UNLIKELY(engine->hasErrorOrException(sv)))
- throw ErrorInfo(engine->lastErrorString(sv), configureScript->location());
+ ScopedJsValue sv(ctx, engine->evaluate(JsValueOwner::Caller,
+ configureScript->sourceCodeForEvaluation(), {}, 1,
+ {fileCtxScopes.fileScope, fileCtxScopes.importScope, configureScope}));
+ if (JsException ex = engine->checkAndClearException(configureScript->location()))
+ throw ex.toErrorInfo();
importedFilesUsedInConfigure = engine->importedFilesUsedInScript();
} else {
importedFilesUsedInConfigure = resolvedProbe->importedFilesUsed();
@@ -199,17 +198,28 @@ void ProbesResolver::resolveProbe(ModuleLoader::ProductContext *productContext,
newValue = resolvedProbe->properties().value(b.first);
} else {
if (condition) {
- QScriptValue v = configureScope.property(b.first);
- m_evaluator->convertToPropertyType(probe->propertyDeclaration(
- b.first), probe->location(), v);
- if (Q_UNLIKELY(engine->hasErrorOrException(v)))
- throw ErrorInfo(engine->lastError(v));
- newValue = v.toVariant();
+ JSValue v = getJsProperty(ctx, configureScope, b.first);
+ const JSValue saved = v;
+ ScopedJsValue valueMgr(ctx, saved);
+ const PropertyDeclaration decl = probe->propertyDeclaration(b.first);
+ m_evaluator->convertToPropertyType(decl, probe->location(), v);
+
+ // If the value was converted from scalar to array as per our convenience
+ // functionality, then the original value is now the only element of a
+ // newly allocated array and thus gets deleted via that array.
+ // The array itself is owned by the script engine, so we must stay out of
+ // memory management here.
+ if (v != saved)
+ valueMgr.setValue(JS_UNDEFINED);
+
+ if (JsException ex = engine->checkAndClearException({}))
+ throw ex.toErrorInfo();
+ newValue = getJsVariant(ctx, v);
} else {
newValue = initialProperties.value(b.first);
}
}
- if (newValue != b.second.toVariant())
+ if (newValue != getJsVariant(ctx, b.second))
probe->setProperty(b.first, VariantValue::create(newValue));
if (!resolvedProbe)
properties.insert(b.first, newValue);
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
index 208f2c5a6..c2c8ba134 100644
--- a/src/lib/corelib/language/projectresolver.cpp
+++ b/src/lib/corelib/language/projectresolver.cpp
@@ -67,6 +67,8 @@
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
+#include <quickjs.h>
+
#include <QtCore/qdir.h>
#include <QtCore/qregularexpression.h>
@@ -326,7 +328,8 @@ void ProjectResolver::resolveProjectFully(Item *item, ProjectResolver::ProjectCo
continue;
const ValueConstPtr v = item->property(it.key());
QBS_ASSERT(v && v->type() != Value::ItemValueType, continue);
- projectProperties.insert(it.key(), m_evaluator->value(item, it.key()).toVariant());
+ const ScopedJsValue sv(m_engine->context(), m_evaluator->value(item, it.key()));
+ projectProperties.insert(it.key(), getJsVariant(m_engine->context(), sv));
}
projectContext->project->setProjectProperties(projectProperties);
@@ -475,7 +478,7 @@ void ProjectResolver::resolveProductFully(Item *item, ProjectContext *projectCon
createProductConfig(product.get());
product->productProperties.insert(StringConstants::destinationDirProperty(),
product->destinationDirectory);
- ModuleProperties::init(m_evaluator->scriptValue(item), product.get());
+ ModuleProperties::init(m_evaluator->engine(), m_evaluator->scriptValue(item), product.get());
QList<Item *> subItems = item->children();
const ValuePtr filesProperty = item->property(StringConstants::filesProperty());
@@ -1213,9 +1216,10 @@ void ProjectResolver::resolveRule(Item *item, ProjectContext *projectContext)
}
rule->name = m_evaluator->stringValue(item, StringConstants::nameProperty());
- rule->prepareScript.initialize(scriptFunctionValue(item, StringConstants::prepareProperty()));
- rule->outputArtifactsScript.initialize(scriptFunctionValue(
- item, StringConstants::outputArtifactsProperty()));
+ rule->prepareScript.initialize(
+ scriptFunctionValue(item, StringConstants::prepareProperty()));
+ rule->outputArtifactsScript.initialize(
+ scriptFunctionValue(item, StringConstants::outputArtifactsProperty()));
rule->outputFileTags = m_evaluator->fileTagsValue(
item, StringConstants::outputFileTagsProperty());
if (rule->outputArtifactsScript.isValid()) {
@@ -1395,9 +1399,10 @@ void ProjectResolver::resolveScanner(Item *item, ProjectResolver::ProjectContext
scanner->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
scanner->inputs = m_evaluator->fileTagsValue(item, StringConstants::inputsProperty());
scanner->recursive = m_evaluator->boolValue(item, StringConstants::recursiveProperty());
- scanner->searchPathsScript.initialize(scriptFunctionValue(
- item, StringConstants::searchPathsProperty()));
- scanner->scanScript.initialize(scriptFunctionValue(item, StringConstants::scanProperty()));
+ scanner->searchPathsScript.initialize(
+ scriptFunctionValue(item, StringConstants::searchPathsProperty()));
+ scanner->scanScript.initialize(
+ scriptFunctionValue(item, StringConstants::scanProperty()));
m_productContext->product->scanners.push_back(scanner);
}
@@ -1739,6 +1744,7 @@ QVariantMap ProjectResolver::evaluateProperties(const Item *item, const Item *pr
void ProjectResolver::evaluateProperty(const Item *item, const QString &propName,
const ValuePtr &propValue, QVariantMap &result, bool checkErrors)
{
+ JSContext * const ctx = m_engine->context();
switch (propValue->type()) {
case Value::ItemValueType:
{
@@ -1754,23 +1760,24 @@ void ProjectResolver::evaluateProperty(const Item *item, const QString &propName
if (pd.flags().testFlag(PropertyDeclaration::PropertyNotAvailableInConfig)) {
break;
}
- const QScriptValue scriptValue = m_evaluator->property(item, propName);
- if (checkErrors && Q_UNLIKELY(m_evaluator->engine()->hasErrorOrException(scriptValue))) {
- throw ErrorInfo(m_evaluator->engine()->lastError(scriptValue,
- propValue->location()));
+ const ScopedJsValue scriptValue(ctx, m_evaluator->property(item, propName));
+ if (JsException ex = m_evaluator->engine()->checkAndClearException(propValue->location())) {
+ if (checkErrors)
+ throw ex.toErrorInfo();
}
// NOTE: Loses type information if scriptValue.isUndefined == true,
// as such QScriptValues become invalid QVariants.
QVariant v;
- if (scriptValue.isFunction()) {
- v = scriptValue.toString();
+ if (JS_IsFunction(ctx, scriptValue)) {
+ v = getJsString(ctx, scriptValue);
} else {
- v = scriptValue.toVariant();
+ v = getJsVariant(ctx, scriptValue);
QVariantMap m = v.toMap();
if (m.contains(StringConstants::importScopeNamePropertyInternal())) {
QVariantMap tmp = m;
- m = scriptValue.prototype().toVariant().toMap();
+ const ScopedJsValue proto(ctx, JS_GetPrototype(ctx, scriptValue));
+ m = getJsVariant(ctx, proto).toMap();
for (auto it = tmp.begin(); it != tmp.end(); ++it)
m.insert(it.key(), it.value());
v = m;
@@ -1855,7 +1862,7 @@ void ProjectResolver::collectPropertiesForExportItem(const QualifiedId &moduleNa
{
valueItem->setScope(moduleInstance);
if (!hadName) {
- // EvaluatorScriptClass expects a name here.
+ // Evaluator expects a name here.
valueItem->setProperty(StringConstants::nameProperty(),
VariantValue::create(moduleName.toString()));
}
diff --git a/src/lib/corelib/language/propertydeclaration.cpp b/src/lib/corelib/language/propertydeclaration.cpp
index 05b2a7f55..f2ac019e9 100644
--- a/src/lib/corelib/language/propertydeclaration.cpp
+++ b/src/lib/corelib/language/propertydeclaration.cpp
@@ -281,7 +281,6 @@ ErrorInfo PropertyDeclaration::checkForDeprecation(DeprecationWarningMode mode,
return deprecationInfo().checkForDeprecation(mode, name(), loc, false, logger);
}
-// see also: EvaluatorScriptClass::convertToPropertyType()
QVariant PropertyDeclaration::convertToPropertyType(const QVariant &v, Type t,
const QStringList &namePrefix, const QString &key)
{
diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp
index adf1a61cc..6be9de4d4 100644
--- a/src/lib/corelib/language/scriptengine.cpp
+++ b/src/lib/corelib/language/scriptengine.cpp
@@ -46,12 +46,14 @@
#include "preparescriptobserver.h"
#include <buildgraph/artifact.h>
+#include <buildgraph/rulenode.h>
#include <jsextensions/jsextensions.h>
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
#include <tools/profiling.h>
#include <tools/qbsassert.h>
+#include <tools/scripttools.h>
#include <tools/qttools.h>
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
@@ -63,18 +65,15 @@
#include <QtCore/qtextstream.h>
#include <QtCore/qtimer.h>
-#include <QtScript/qscriptclass.h>
-#include <QtScript/qscriptvalueiterator.h>
-
+#include <cstring>
#include <functional>
#include <set>
#include <utility>
+#include <vector>
namespace qbs {
namespace Internal {
-static QString getterFuncHelperProperty() { return QStringLiteral("qbsdata"); }
-
const bool debugJSImports = false;
bool operator==(const ScriptEngine::PropertyCacheKey &lhs,
@@ -85,9 +84,7 @@ bool operator==(const ScriptEngine::PropertyCacheKey &lhs,
&& lhs.m_propertyName == rhs.m_propertyName;
}
-
-
-static inline QHashValueType combineHash(QHashValueType h1, QHashValueType h2, QHashValueType seed)
+static QHashValueType combineHash(QHashValueType h1, QHashValueType h2, QHashValueType seed)
{
// stolen from qHash(QPair)
return ((h1 << 16) | (h1 >> 16)) ^ h2 ^ seed;
@@ -99,52 +96,140 @@ QHashValueType qHash(const ScriptEngine::PropertyCacheKey &k, QHashValueType see
combineHash(qHash(k.m_propertyName), qHash(k.m_propertyMap), seed), seed);
}
-std::mutex ScriptEngine::m_creationDestructionMutex;
-
ScriptEngine::ScriptEngine(Logger &logger, EvalContext evalContext, PrivateTag)
: m_scriptImporter(new ScriptImporter(this)),
- m_modulePropertyScriptClass(nullptr),
- m_propertyCacheEnabled(true), m_active(false), m_logger(logger), m_evalContext(evalContext),
+ m_logger(logger), m_evalContext(evalContext),
m_observer(new PrepareScriptObserver(this, UnobserveMode::Disabled))
{
- setProcessEventsInterval(1000); // For the cancelation mechanism to work.
- m_cancelationError = currentContext()->throwValue(tr("Execution canceled"));
- QScriptValue objectProto = globalObject().property(QStringLiteral("Object"));
- m_definePropertyFunction = objectProto.property(QStringLiteral("defineProperty"));
- QBS_ASSERT(m_definePropertyFunction.isFunction(), /* ignore */);
- m_emptyFunction = evaluate(QStringLiteral("(function(){})"));
- QBS_ASSERT(m_emptyFunction.isFunction(), /* ignore */);
- // Initially push a new context to turn off scope chain insanity mode.
- QScriptEngine::pushContext();
+ setMaxStackSize();
+ JS_SetRuntimeOpaque(m_jsRuntime, this);
+ JS_SetInterruptHandler(m_jsRuntime, interruptor, this);
+ setScopeLookup(m_context, &ScriptEngine::doExtraScopeLookup);
+ setFoundUndefinedHandler(m_context, &ScriptEngine::handleUndefinedFound);
+ setFunctionEnteredHandler(m_context, &ScriptEngine::handleFunctionEntered);
+ setFunctionExitedHandler(m_context, &ScriptEngine::handleFunctionExited);
+ m_dataWithPtrClass = registerClass("__data", nullptr, nullptr, JS_UNDEFINED);
installQbsBuiltins();
extendJavaScriptBuiltins();
}
std::unique_ptr<ScriptEngine> ScriptEngine::create(Logger &logger, EvalContext evalContext)
{
- std::lock_guard<std::mutex> lock(m_creationDestructionMutex);
return std::make_unique<ScriptEngine>(logger, evalContext, PrivateTag());
}
-ScriptEngine::~ScriptEngine()
+ScriptEngine *ScriptEngine::engineForRuntime(const JSRuntime *runtime)
+{
+ return static_cast<ScriptEngine *>(JS_GetRuntimeOpaque(const_cast<JSRuntime *>(runtime)));
+
+}
+
+ScriptEngine *ScriptEngine::engineForContext(const JSContext *ctx)
+{
+ return engineForRuntime(JS_GetRuntime(const_cast<JSContext *>(ctx)));
+}
+
+LookupResult ScriptEngine::doExtraScopeLookup(JSContext *ctx, JSAtom prop)
+{
+ static const LookupResult fail{JS_UNDEFINED, JS_UNDEFINED, false};
+
+ ScriptEngine * const engine = engineForContext(ctx);
+ engine->m_lastLookupWasSuccess = false;
+ JSValueList scopes;
+
+ // FIXME: This is probably wrong, and kept only for "bug compatibility"
+ // The correct code should be the one commented out below. Fix for 2.1.
+ for (const auto &l : qAsConst(engine->m_scopeChains)) {
+ for (const auto &s : l)
+ scopes.push_back(s);
+ }
+ // if (!engine->m_scopeChains.isEmpty())
+ // scopes = engine->m_scopeChains.last();
+
+ if (JS_IsObject(engine->m_globalObject))
+ scopes.insert(scopes.begin(), engine->m_globalObject);
+ for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) {
+ const JSValue v = JS_GetProperty(ctx, *it, prop);
+ if (!JS_IsUndefined(v) || engine->m_lastLookupWasSuccess) {
+ engine->m_lastLookupWasSuccess = false;
+ return {v, *it, true};
+ }
+ }
+ return fail;
+}
+
+void ScriptEngine::handleUndefinedFound(JSContext *ctx)
{
- m_creationDestructionMutex.lock();
- connect(this, &QObject::destroyed, []{ m_creationDestructionMutex.unlock(); });
+ engineForContext(ctx)->setLastLookupStatus(true);
+}
+
+void ScriptEngine::handleFunctionEntered(JSContext *ctx, JSValue this_obj)
+{
+ ScriptEngine::engineForContext(ctx)->m_contextStack.push_back(this_obj);
+}
- releaseResourcesOfScriptObjects();
- delete (m_scriptImporter);
+void ScriptEngine::handleFunctionExited(JSContext *ctx)
+{
+ ScriptEngine::engineForContext(ctx)->m_contextStack.pop_back();
+}
+
+ScriptEngine::~ScriptEngine()
+{
+ reset();
+ delete m_scriptImporter;
if (m_elapsedTimeImporting != -1) {
m_logger.qbsLog(LoggerInfo, true) << Tr::tr("Setting up imports took %1.")
.arg(elapsedTimeString(m_elapsedTimeImporting));
}
- delete m_modulePropertyScriptClass;
- delete m_productPropertyScriptClass;
+ for (const auto &ext : qAsConst(m_internalExtensions))
+ JS_FreeValue(m_context, ext);
+ for (const JSValue &s : qAsConst(m_stringCache))
+ JS_FreeValue(m_context, s);
+ setPropertyOnGlobalObject(QLatin1String("console"), JS_UNDEFINED);
+ JS_FreeContext(m_context);
+ JS_FreeRuntime(m_jsRuntime);
}
-void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue &targetObject,
+void ScriptEngine::reset()
+{
+ // TODO: Check whether we can keep file and imports cache.
+ // We'd have to find a solution for the scope name problem then.
+ clearImportsCache();
+ for (const auto &e : qAsConst(m_jsFileCache))
+ JS_FreeValue(m_context, e.second);
+ m_jsFileCache.clear();
+
+ for (auto it = m_evalResults.cbegin(); it != m_evalResults.cend(); ++it) {
+ for (int i = 0; i < it.value(); ++i)
+ JS_FreeValue(m_context, it.key());
+ }
+ m_evalResults.clear();
+ for (const auto &e : qAsConst(m_projectScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_projectScriptValues.clear();
+ for (const auto &e : qAsConst(m_baseProductScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_baseProductScriptValues.clear();
+ for (const auto &e : qAsConst(m_productArtifactsMapScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_productArtifactsMapScriptValues.clear();
+ for (const auto &e : qAsConst(m_moduleArtifactsMapScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_moduleArtifactsMapScriptValues.clear();
+ for (const auto &e : qAsConst(m_baseModuleScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_baseModuleScriptValues.clear();
+ for (auto it = m_artifactsScriptValues.cbegin(); it != m_artifactsScriptValues.cend(); ++it) {
+ it.key().first->setDeregister({});
+ JS_FreeValue(m_context, it.value());
+ }
+ m_artifactsScriptValues.clear();
+}
+
+void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, JSValue &targetObject,
ObserveMode observeMode)
{
- installImportFunctions();
+ installImportFunctions(targetObject);
m_currentDirPathStack.push(FileInfo::path(fileCtx->filePath()));
m_extensionSearchPathsStack.push(fileCtx->searchPaths());
m_observeMode = observeMode;
@@ -152,7 +237,7 @@ void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue &
for (const JsImport &jsImport : fileCtx->jsImports())
import(jsImport, targetObject);
if (m_observeMode == ObserveMode::Enabled) {
- for (QScriptValue &sv : m_requireResults)
+ for (JSValue &sv : m_requireResults)
observeImport(sv);
m_requireResults.clear();
}
@@ -162,58 +247,72 @@ void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue &
uninstallImportFunctions();
}
-void ScriptEngine::import(const JsImport &jsImport, QScriptValue &targetObject)
+void ScriptEngine::import(const JsImport &jsImport, JSValue &targetObject)
{
- QBS_ASSERT(targetObject.isObject(), return);
- QBS_ASSERT(targetObject.engine() == this, return);
+ QBS_ASSERT(JS_IsObject(targetObject), return);
if (debugJSImports)
qDebug() << "[ENGINE] import into " << jsImport.scopeName;
- QScriptValue jsImportValue = m_jsImportCache.value(jsImport);
- if (jsImportValue.isValid()) {
+ JSValue jsImportValue = m_jsImportCache.value(jsImport);
+ if (JS_IsObject(jsImportValue)) {
if (debugJSImports)
qDebug() << "[ENGINE] " << jsImport.filePaths << " (cache hit)";
} else {
if (debugJSImports)
qDebug() << "[ENGINE] " << jsImport.filePaths << " (cache miss)";
- jsImportValue = newObject();
+
+ jsImportValue = JS_NewObject(m_context);
for (const QString &filePath : jsImport.filePaths)
importFile(filePath, jsImportValue);
m_jsImportCache.insert(jsImport, jsImportValue);
std::vector<QString> &filePathsForScriptValue
- = m_filePathsPerImport[jsImportValue.objectId()];
+ = m_filePathsPerImport[jsObjectId(jsImportValue)];
transform(jsImport.filePaths, filePathsForScriptValue, [](const auto &fp) {
return fp; });
}
- QScriptValue sv = newObject();
- sv.setPrototype(jsImportValue);
- sv.setProperty(StringConstants::importScopeNamePropertyInternal(), jsImport.scopeName);
- targetObject.setProperty(jsImport.scopeName, sv);
+ JSValue sv = JS_NewObjectProto(m_context, jsImportValue);
+ setJsProperty(m_context, sv, StringConstants::importScopeNamePropertyInternal(),
+ jsImport.scopeName);
+ setJsProperty(m_context, targetObject, jsImport.scopeName, sv);
if (m_observeMode == ObserveMode::Enabled)
observeImport(jsImportValue);
}
-void ScriptEngine::observeImport(QScriptValue &jsImport)
+void ScriptEngine::observeImport(JSValue &jsImport)
{
- if (!m_observer->addImportId(jsImport.objectId()))
+ if (!m_observer->addImportId(quintptr((JS_VALUE_GET_OBJ(jsImport)))))
return;
- QScriptValueIterator it(jsImport);
- while (it.hasNext()) {
- it.next();
- if (it.flags() & QScriptValue::PropertyGetter)
- continue;
- QScriptValue property = it.value();
- if (!property.isFunction())
- continue;
- setObservedProperty(jsImport, it.name(), property);
- }
+ handleJsProperties(jsImport, [this, &jsImport](const JSAtom &name,
+ const JSPropertyDescriptor &desc) {
+ if (!JS_IsFunction(m_context, desc.value))
+ return;
+ const char *const nameStr = JS_AtomToCString(m_context, name);
+ setObservedProperty(jsImport, QString::fromUtf8(nameStr, std::strlen(nameStr)), desc.value);
+ JS_FreeCString(m_context, nameStr);
+ });
}
void ScriptEngine::clearImportsCache()
{
+ for (const auto &jsImport : qAsConst(m_jsImportCache))
+ JS_FreeValue(m_context, jsImport);
m_jsImportCache.clear();
+ m_filePathsPerImport.clear();
+ m_observer->clearImportIds();
+}
+
+void ScriptEngine::registerEvaluator(Evaluator *evaluator)
+{
+ QBS_ASSERT(!m_evaluator, return);
+ m_evaluator = evaluator;
+}
+
+void ScriptEngine::unregisterEvaluator(const Evaluator *evaluator)
+{
+ QBS_ASSERT(m_evaluator == evaluator, return);
+ m_evaluator = nullptr;
}
void ScriptEngine::checkContext(const QString &operation,
@@ -242,7 +341,15 @@ void ScriptEngine::checkContext(const QString &operation,
QBS_ASSERT(false, continue);
break;
}
- m_logger.printWarning(ErrorInfo(warning, currentContext()->backtrace()));
+ if (!m_evalPositions.empty()) {
+ const JSValue exVal = JS_NewObject(m_context);
+ const auto &[file, line] = m_evalPositions.top();
+ build_backtrace(m_context, exVal, file.toUtf8().constData(), line, 0);
+ const JsException ex(m_context, exVal, {});
+ m_logger.printWarning(ErrorInfo(warning, ex.stackTrace()));
+ } else {
+ m_logger.printWarning(ErrorInfo(warning));
+ }
return;
}
}
@@ -253,7 +360,7 @@ void ScriptEngine::addPropertyRequestedFromArtifact(const Artifact *artifact,
m_propertiesRequestedFromArtifact[artifact->filePath()] << property;
}
-void ScriptEngine::addImportRequestedInScript(qint64 importValueId)
+void ScriptEngine::addImportRequestedInScript(quintptr importValueId)
{
// Import list is assumed to be small, so let's not use a set.
if (!contains(m_importsRequestedInScript, importValueId))
@@ -291,37 +398,21 @@ QVariant ScriptEngine::retrieveFromPropertyCache(const QString &moduleName,
return m_propertyCache.value(PropertyCacheKey(moduleName, propertyName, propertyMap));
}
-void ScriptEngine::defineProperty(QScriptValue &object, const QString &name,
- const QScriptValue &descriptor)
+static JSValue js_observedGet(JSContext *ctx, JSValueConst, int, JSValueConst *, int, JSValue *data)
{
- QScriptValue arguments = newArray();
- arguments.setProperty(0, object);
- arguments.setProperty(1, name);
- arguments.setProperty(2, descriptor);
- QScriptValue result = m_definePropertyFunction.call(QScriptValue(), arguments);
- QBS_ASSERT(!hasErrorOrException(result), qDebug() << name << result.toString());
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ engine->observer()->onPropertyRead(data[0], getJsString(ctx, data[1]), data[2]);
+ return JS_DupValue(engine->context(), data[2]);
}
-static QScriptValue js_observedGet(QScriptContext *context, QScriptEngine *,
- ScriptPropertyObserver * const observer)
+void ScriptEngine::setObservedProperty(JSValue &object, const QString &name,
+ const JSValue &value)
{
- const QScriptValue data = context->callee().property(getterFuncHelperProperty());
- const QScriptValue value = data.property(2);
- observer->onPropertyRead(data.property(0), data.property(1).toVariant().toString(), value);
- return value;
-}
-
-void ScriptEngine::setObservedProperty(QScriptValue &object, const QString &name,
- const QScriptValue &value)
-{
- QScriptValue data = newArray();
- data.setProperty(0, object);
- data.setProperty(1, name);
- data.setProperty(2, value);
- QScriptValue getterFunc = newFunction(js_observedGet,
- static_cast<ScriptPropertyObserver *>(m_observer.get()));
- getterFunc.setProperty(getterFuncHelperProperty(), data);
- object.setProperty(name, getterFunc, QScriptValue::PropertyGetter);
+ ScopedJsValue jsName(m_context, makeJsString(m_context, name));
+ JSValueList funcData{object, jsName, value};
+ JSValue getterFunc = JS_NewCFunctionData(m_context, &js_observedGet, 0, 0, 3, funcData.data());
+ const ScopedJsAtom nameAtom(m_context, name);
+ JS_DefinePropertyGetSet(m_context, object, nameAtom, getterFunc, JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
if (m_observer->unobserveMode() == UnobserveMode::Enabled)
m_observedProperties.emplace_back(object, name, value);
}
@@ -329,38 +420,16 @@ void ScriptEngine::setObservedProperty(QScriptValue &object, const QString &name
void ScriptEngine::unobserveProperties()
{
for (auto &elem : m_observedProperties) {
- QScriptValue &object = std::get<0>(elem);
+ JSValue &object = std::get<0>(elem);
const QString &name = std::get<1>(elem);
- const QScriptValue &value = std::get<2>(elem);
- object.setProperty(name, QScriptValue(), QScriptValue::PropertyGetter);
- object.setProperty(name, value, QScriptValue::PropertyFlags());
+ const JSValue &value = std::get<2>(elem);
+ const ScopedJsAtom jsName(m_context, name);
+ JS_DefineProperty(m_context, object, jsName, value, JS_UNDEFINED, JS_UNDEFINED,
+ JS_PROP_HAS_VALUE);
}
m_observedProperties.clear();
}
-static QScriptValue js_deprecatedGet(QScriptContext *context, QScriptEngine *qtengine)
-{
- const auto engine = static_cast<const ScriptEngine *>(qtengine);
- const QScriptValue data = context->callee().property(getterFuncHelperProperty());
- engine->logger().qbsWarning()
- << ScriptEngine::tr("Property %1 is deprecated. Please use %2 instead.").arg(
- data.property(0).toString(), data.property(1).toString());
- return data.property(2);
-}
-
-void ScriptEngine::setDeprecatedProperty(QScriptValue &object, const QString &oldName,
- const QString &newName, const QScriptValue &value)
-{
- QScriptValue data = newArray();
- data.setProperty(0, oldName);
- data.setProperty(1, newName);
- data.setProperty(2, value);
- QScriptValue getterFunc = newFunction(js_deprecatedGet);
- getterFunc.setProperty(getterFuncHelperProperty(), data);
- object.setProperty(oldName, getterFunc, QScriptValue::PropertyGetter
- | QScriptValue::SkipInEnumeration);
-}
-
QProcessEnvironment ScriptEngine::environment() const
{
return m_environment;
@@ -371,17 +440,17 @@ void ScriptEngine::setEnvironment(const QProcessEnvironment &env)
m_environment = env;
}
-void ScriptEngine::importFile(const QString &filePath, QScriptValue &targetObject)
+void ScriptEngine::importFile(const QString &filePath, JSValue &targetObject)
{
AccumulatingTimer importTimer(m_elapsedTimeImporting != -1 ? &m_elapsedTimeImporting : nullptr);
- QScriptValue &evaluationResult = m_jsFileCache[filePath];
- if (evaluationResult.isValid()) {
- ScriptImporter::copyProperties(evaluationResult, targetObject);
+ JSValue &evaluationResult = m_jsFileCache[filePath];
+ if (JS_IsObject(evaluationResult)) {
+ ScriptImporter::copyProperties(m_context, evaluationResult, targetObject);
return;
}
QFile file(filePath);
if (Q_UNLIKELY(!file.open(QFile::ReadOnly)))
- throw ErrorInfo(tr("Cannot open '%1'.").arg(filePath));
+ throw ErrorInfo(Tr::tr("Cannot open '%1'.").arg(filePath));
QTextStream stream(&file);
setupDefaultCodec(stream);
const QString sourceCode = stream.readAll();
@@ -402,83 +471,104 @@ static QString findExtensionDir(const QStringList &searchPaths, const QString &e
return {};
}
-static QScriptValue mergeExtensionObjects(const QScriptValueList &lst)
+JSValue ScriptEngine::mergeExtensionObjects(const JSValueList &lst)
{
- QScriptValue result;
- for (const QScriptValue &v : lst) {
- if (!result.isValid()) {
+ JSValue result = JS_UNDEFINED;
+ for (const JSValue &v : lst) {
+ if (!JS_IsObject(result)) {
result = v;
continue;
}
- QScriptValueIterator svit(v);
- while (svit.hasNext()) {
- svit.next();
- result.setProperty(svit.name(), svit.value());
- }
+ ScriptImporter::copyProperties(m_context, v, result);
+ JS_FreeValue(m_context, v);
}
return result;
}
-static QScriptValue loadInternalExtension(QScriptContext *context, ScriptEngine *engine,
- const QString &uri)
+JSValue ScriptEngine::getInternalExtension(const char *name) const
{
- const QString name = uri.mid(4); // remove the "qbs." part
- QScriptValue extensionObj = JsExtensions::loadExtension(engine, name);
- if (!extensionObj.isValid()) {
- return context->throwError(ScriptEngine::tr("loadExtension: "
- "cannot load extension '%1'.").arg(uri));
- }
- return extensionObj;
+ const auto cached = m_internalExtensions.constFind(QLatin1String(name));
+ if (cached != m_internalExtensions.constEnd())
+ return JS_DupValue(m_context, cached.value());
+ return JS_UNDEFINED;
}
-QScriptValue ScriptEngine::js_loadExtension(QScriptContext *context, QScriptEngine *qtengine)
+void ScriptEngine::addInternalExtension(const char *name, JSValue ext)
{
- if (context->argumentCount() < 1) {
- return context->throwError(
- ScriptEngine::tr("The loadExtension function requires "
- "an extension name."));
- }
+ m_internalExtensions.insert(QLatin1String(name), JS_DupValue(m_context, ext));
+}
+
+JSValue ScriptEngine::asJsValue(const QString &s)
+{
+ const auto it = m_stringCache.constFind(s);
+ if (it != m_stringCache.constEnd())
+ return JS_DupValue(m_context, it.value());
+ const JSValue sv = JS_NewString(m_context, s.toUtf8().constData());
+ m_stringCache.insert(s, sv);
+ return JS_DupValue(m_context, sv);
+}
- const auto engine = static_cast<const ScriptEngine *>(qtengine);
- ErrorInfo deprWarning(Tr::tr("The loadExtension() function is deprecated and will be "
- "removed in a future version of Qbs. Use require() "
- "instead."), context->backtrace());
- engine->logger().printWarning(deprWarning);
+JSValue ScriptEngine::asJsValue(const QStringList &l)
+{
+ JSValue array = JS_NewArray(m_context);
+ setJsProperty(m_context, array, QLatin1String("length"), JS_NewInt32(m_context, l.size()));
+ for (int i = 0; i < l.size(); ++i)
+ JS_SetPropertyUint32(m_context, array, i, asJsValue(l.at(i)));
+ return array;
+}
- return js_require(context, qtengine);
+JSValue ScriptEngine::asJsValue(const QVariantMap &m)
+{
+ JSValue obj = JS_NewObject(m_context);
+ for (auto it = m.begin(); it != m.end(); ++it)
+ setJsProperty(m_context, obj, it.key(), makeJsVariant(m_context, it.value()));
+ return obj;
}
-QScriptValue ScriptEngine::js_loadFile(QScriptContext *context, QScriptEngine *qtengine)
+void ScriptEngine::setPropertyOnGlobalObject(const QString &property, JSValue value)
{
- if (context->argumentCount() < 1) {
- return context->throwError(
- ScriptEngine::tr("The loadFile function requires a file path."));
- }
+ const ScopedJsValue globalObject(m_context, JS_GetGlobalObject(m_context));
+ setJsProperty(m_context, globalObject, property, value);
+}
- const auto engine = static_cast<const ScriptEngine *>(qtengine);
- ErrorInfo deprWarning(Tr::tr("The loadFile() function is deprecated and will be "
- "removed in a future version of Qbs. Use require() "
- "instead."), context->backtrace());
- engine->logger().printWarning(deprWarning);
+JSValue ScriptEngine::asJsValue(const QVariantList &l)
+{
+ JSValue array = JS_NewArray(m_context);
+ setJsProperty(m_context, array, QLatin1String("length"), JS_NewInt32(m_context, l.size()));
+ for (int i = 0; i < l.size(); ++i)
+ JS_SetPropertyUint32(m_context, array, i, makeJsVariant(m_context, l.at(i)));
+ return array;
+}
- return js_require(context, qtengine);
+JSValue ScriptEngine::loadInternalExtension(const QString &uri)
+{
+ const QString name = uri.mid(4); // remove the "qbs." part
+ const auto cached = m_internalExtensions.constFind(name);
+ if (cached != m_internalExtensions.constEnd())
+ return cached.value();
+ JSValue extensionObj = JsExtensions::loadExtension(this, name);
+ if (!JS_IsObject(extensionObj))
+ return throwError(Tr::tr("loadExtension: cannot load extension '%1'.").arg(uri));
+ m_internalExtensions.insert(name, extensionObj);
+ return extensionObj;
}
-QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qtengine)
+JSValue ScriptEngine::js_require(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int, JSValue *func_data)
{
- const auto engine = static_cast<ScriptEngine *>(qtengine);
- if (context->argumentCount() < 1) {
- return context->throwError(
- ScriptEngine::tr("The require function requires a module name or path."));
- }
+ Q_UNUSED(this_val)
+
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ QBS_ASSERT(engine, return JS_EXCEPTION);
+ if (argc < 1)
+ return engine->throwError(Tr::tr("The require function requires a module name or path."));
- const QString moduleName = context->argument(0).toString();
+ const QString moduleName = getJsString(ctx, argv[0]);
// First try to load a named module if the argument doesn't look like a file path
if (!moduleName.contains(QLatin1Char('/'))) {
if (engine->m_extensionSearchPathsStack.empty())
- return context->throwError(
- ScriptEngine::tr("require: internal error. No search paths."));
+ return engine->throwError(Tr::tr("require: internal error. No search paths."));
if (engine->m_logger.debugEnabled()) {
engine->m_logger.qbsDebug()
@@ -491,11 +581,11 @@ QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qt
const QString dirPath = findExtensionDir(searchPaths, moduleNameAsPath);
if (dirPath.isEmpty()) {
if (moduleName.startsWith(QStringLiteral("qbs.")))
- return loadInternalExtension(context, engine, moduleName);
+ return JS_DupValue(ctx, engine->loadInternalExtension(moduleName));
} else {
QDirIterator dit(dirPath, StringConstants::jsFileWildcards(),
QDir::Files | QDir::Readable);
- QScriptValueList values;
+ JSValueList values;
std::vector<QString> filePaths;
try {
while (dit.hasNext()) {
@@ -504,19 +594,19 @@ QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qt
engine->m_logger.qbsDebug()
<< "[require] importing file " << filePath;
}
- QScriptValue obj = engine->newObject();
+ JSValue obj = engine->newObject();
engine->importFile(filePath, obj);
values << obj;
filePaths.push_back(filePath);
}
} catch (const ErrorInfo &e) {
- return context->throwError(e.toString());
+ return engine->throwError(e.toString());
}
if (!values.empty()) {
- const QScriptValue mergedValue = mergeExtensionObjects(values);
+ const JSValue mergedValue = engine->mergeExtensionObjects(values);
engine->m_requireResults.push_back(mergedValue);
- engine->m_filePathsPerImport[mergedValue.objectId()] = filePaths;
+ engine->m_filePathsPerImport[jsObjectId(mergedValue)] = filePaths;
return mergedValue;
}
}
@@ -525,52 +615,87 @@ QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qt
// file located in the current directory search path; try that next
}
- if (engine->m_currentDirPathStack.empty()) {
- return context->throwError(
- ScriptEngine::tr("require: internal error. No current directory."));
- }
+ if (engine->m_currentDirPathStack.empty())
+ return engine->throwError(Tr::tr("require: internal error. No current directory."));
- QScriptValue result;
+ JSValue result;
try {
const QString filePath = FileInfo::resolvePath(engine->m_currentDirPathStack.top(),
moduleName);
- result = engine->newObject();
- engine->importFile(filePath, result);
static const QString scopeNamePrefix = QStringLiteral("_qbs_scope_");
const QString scopeName = scopeNamePrefix + QString::number(qHash(filePath), 16);
- result.setProperty(StringConstants::importScopeNamePropertyInternal(), scopeName);
- context->thisObject().setProperty(scopeName, result);
+ result = getJsProperty(ctx, func_data[0], scopeName);
+ if (JS_IsObject(result))
+ return result; // Same JS file imported from same qbs file via different JS files (e.g. codesign.js from DarwinGCC.qbs via gcc.js and darwin.js).
+ result = engine->newObject();
+ engine->importFile(filePath, result);
+ setJsProperty(ctx, result, StringConstants::importScopeNamePropertyInternal(), scopeName);
+ setJsProperty(ctx, func_data[0], scopeName, result);
engine->m_requireResults.push_back(result);
- engine->m_filePathsPerImport[result.objectId()] = { filePath };
+ engine->m_filePathsPerImport[jsObjectId(JS_DupValue(ctx, result))] = { filePath };
} catch (const ErrorInfo &e) {
- result = context->throwError(e.toString());
+ result = engine->throwError(e.toString());
}
return result;
}
-QScriptClass *ScriptEngine::modulePropertyScriptClass() const
+JSClassID ScriptEngine::modulePropertyScriptClass() const
{
return m_modulePropertyScriptClass;
}
-void ScriptEngine::setModulePropertyScriptClass(QScriptClass *modulePropertyScriptClass)
+void ScriptEngine::setModulePropertyScriptClass(JSClassID modulePropertyScriptClass)
{
m_modulePropertyScriptClass = modulePropertyScriptClass;
}
-void ScriptEngine::addResourceAcquiringScriptObject(ResourceAcquiringScriptObject *obj)
+template<typename T> JSValue getScriptValue(JSContext *ctx, const T *t,
+ const std::unordered_map<const T *, JSValue> &map)
{
- m_resourceAcquiringScriptObjects.push_back(obj);
+ const auto it = map.find(t);
+ return it == map.end() ? JS_UNDEFINED : JS_DupValue(ctx, it->second);
}
-void ScriptEngine::releaseResourcesOfScriptObjects()
+template<typename T> void setScriptValue(JSContext *ctx, const T *t, JSValue value,
+ std::unordered_map<const T *, JSValue> &map)
{
- if (m_resourceAcquiringScriptObjects.empty())
+ value = JS_DupValue(ctx, value);
+ const auto it = map.find(t);
+ if (it == map.end()) {
+ map.insert(std::make_pair(t, value));
return;
- std::for_each(m_resourceAcquiringScriptObjects.begin(), m_resourceAcquiringScriptObjects.end(),
- std::mem_fn(&ResourceAcquiringScriptObject::releaseResources));
- m_resourceAcquiringScriptObjects.clear();
+ }
+ JS_FreeValue(ctx, it->second);
+ it->second = value;
+}
+
+JSValue ScriptEngine::artifactsMapScriptValue(const ResolvedProduct *product)
+{
+ return getScriptValue(m_context, product, m_productArtifactsMapScriptValues);
+}
+
+void ScriptEngine::setArtifactsMapScriptValue(const ResolvedProduct *product, JSValue value)
+{
+ setScriptValue(m_context, product, value, m_productArtifactsMapScriptValues);
+}
+
+JSValue ScriptEngine::artifactsMapScriptValue(const ResolvedModule *module)
+{
+ return getScriptValue(m_context, module, m_moduleArtifactsMapScriptValues);
+}
+
+void ScriptEngine::setArtifactsMapScriptValue(const ResolvedModule *module, JSValue value)
+{
+ setScriptValue(m_context, module, value, m_moduleArtifactsMapScriptValues);
+}
+
+JSValue ScriptEngine::getArtifactProperty(JSValue obj,
+ const std::function<JSValue (const Artifact *)> &propGetter)
+{
+ std::lock_guard lock(m_artifactsMutex);
+ const Artifact * const a = attachedPointer<Artifact>(obj, dataWithPtrClass());
+ return a ? propGetter(a) : JS_EXCEPTION;
}
void ScriptEngine::addCanonicalFilePathResult(const QString &filePath,
@@ -617,49 +742,100 @@ Set<QString> ScriptEngine::imports() const
return filePaths;
}
-QScriptValueList ScriptEngine::argumentList(const QStringList &argumentNames,
- const QScriptValue &context)
+JSValue ScriptEngine::newObject() const
{
- QScriptValueList result;
- for (const auto &name : argumentNames)
- result += context.property(name);
- return result;
+ return JS_NewObject(m_context);
+}
+
+JSValue ScriptEngine::newArray(int length, JsValueOwner owner)
+{
+ JSValue arr = JS_NewArray(m_context);
+ JS_SetPropertyStr(m_context, arr, "length", JS_NewInt32(m_context, length));
+ if (owner == JsValueOwner::ScriptEngine)
+ ++m_evalResults[arr];
+ return arr;
+}
+
+JSValue ScriptEngine::evaluate(JsValueOwner resultOwner, const QString &code,
+ const QString &filePath, int line, const JSValueList &scopeChain)
+{
+ m_scopeChains << scopeChain;
+ const QByteArray &codeStr = code.toUtf8();
+
+ m_evalPositions.emplace(std::make_pair(filePath, line));
+ const JSValue v = JS_EvalThis(m_context, globalObject(), codeStr.constData(), codeStr.length(),
+ filePath.toUtf8().constData(), line, JS_EVAL_TYPE_GLOBAL);
+ m_evalPositions.pop();
+ m_scopeChains.removeLast();
+ if (resultOwner == JsValueOwner::ScriptEngine && JS_VALUE_HAS_REF_COUNT(v))
+ ++m_evalResults[v];
+ return v;
+}
+
+void ScriptEngine::handleJsProperties(JSValue obj, const PropertyHandler &handler)
+{
+ qbs::Internal::handleJsProperties(m_context, obj, handler);
}
-CodeLocation ScriptEngine::lastErrorLocation(const QScriptValue &v,
- const CodeLocation &fallbackLocation) const
+ScopedJsValueList ScriptEngine::argumentList(const QStringList &argumentNames,
+ const JSValue &context) const
{
- const QScriptValue &errorVal = lastErrorValue(v);
- const CodeLocation errorLoc(errorVal.property(StringConstants::fileNameProperty()).toString(),
- errorVal.property(QStringLiteral("lineNumber")).toInt32(),
- errorVal.property(QStringLiteral("expressionCaretOffset")).toInt32(),
- false);
- return errorLoc.isValid() ? errorLoc : fallbackLocation;
+ JSValueList result;
+ for (const auto &name : argumentNames)
+ result.push_back(getJsProperty(m_context, context, name));
+ return ScopedJsValueList(m_context, result);
}
-ErrorInfo ScriptEngine::lastError(const QScriptValue &v, const CodeLocation &fallbackLocation) const
+JSClassID ScriptEngine::registerClass(const char *name, JSClassCall *constructor,
+ JSClassFinalizer *finalizer, JSValue scope,
+ GetPropertyNames getPropertyNames, GetProperty getProperty)
{
- const QString msg = lastErrorString(v);
- CodeLocation errorLocation = lastErrorLocation(v);
- if (errorLocation.isValid())
- return ErrorInfo(msg, errorLocation);
- const QStringList backtrace = uncaughtExceptionBacktraceOrEmpty();
- if (!backtrace.empty()) {
- ErrorInfo e(msg, backtrace);
- if (e.hasLocation())
- return e;
+ JSClassID id = 0;
+ const auto classIt = m_classes.constFind(QLatin1String(name));
+ if (classIt == m_classes.constEnd()) {
+ JS_NewClassID(&id);
+ const auto it = getProperty
+ ? m_exoticMethods.insert(id, JSClassExoticMethods{getProperty, getPropertyNames})
+ : m_exoticMethods.end();
+ JSClassDef jsClass{name, finalizer, nullptr, constructor,
+ it != m_exoticMethods.end() ? &it.value() : nullptr};
+ const int status = JS_NewClass(m_jsRuntime, id, &jsClass);
+ QBS_ASSERT(status == 0, return 0);
+ m_classes.insert(QLatin1String(name), id);
+ } else {
+ id = classIt.value();
}
- return ErrorInfo(msg, fallbackLocation);
+ if (!JS_IsUndefined(scope)) {
+ const JSValue classObj = JS_NewObjectClass(m_context, id);
+ JS_SetConstructorBit(m_context, classObj, constructor != nullptr);
+ JS_SetPropertyStr(m_context, scope, name, classObj);
+ }
+ return id;
+}
+
+JSClassID ScriptEngine::getClassId(const char *name) const
+{
+ return m_classes.value(QLatin1String(name));
+}
+
+JSValue ScriptEngine::throwError(const QString &message) const
+{
+ return qbs::Internal::throwError(m_context, message);
}
void ScriptEngine::cancel()
{
- QTimer::singleShot(0, this, [this] { abort(); });
+ m_canceling = true;
}
-void ScriptEngine::abort()
+int ScriptEngine::interruptor(JSRuntime *, void *opaqueEngine)
{
- abortEvaluation(m_cancelationError);
+ const auto engine = reinterpret_cast<ScriptEngine *>(opaqueEngine);
+ if (engine->m_canceling) {
+ engine->m_canceling = false;
+ return 1;
+ }
+ return 0;
}
bool ScriptEngine::gatherFileResults() const
@@ -668,83 +844,105 @@ bool ScriptEngine::gatherFileResults() const
|| evalContext() == EvalContext::ProbeExecution;
}
+void ScriptEngine::setMaxStackSize()
+{
+ size_t stackSize = 0; // Turn check off by default.
+ bool ok;
+ const int stackSizeFromEnv = qEnvironmentVariableIntValue("QBS_MAX_JS_STACK_SIZE", &ok);
+ if (ok && stackSizeFromEnv >= 0)
+ stackSize = stackSizeFromEnv;
+ JS_SetMaxStackSize(m_jsRuntime, stackSize);
+}
+
+JSValue ScriptEngine::getArtifactScriptValue(Artifact *a, const QString &moduleName,
+ const std::function<void(JSValue obj)> &setup)
+{
+ std::lock_guard lock(m_artifactsMutex);
+ const auto it = m_artifactsScriptValues.constFind(qMakePair(a, moduleName));
+ if (it != m_artifactsScriptValues.constEnd())
+ return JS_DupValue(m_context, *it);
+ a->setDeregister([this](const Artifact *a) {
+ const std::lock_guard lock(m_artifactsMutex);
+ for (auto it = m_artifactsScriptValues.begin(); it != m_artifactsScriptValues.end(); ) {
+ if (it.key().first == a) {
+ JS_SetOpaque(it.value(), nullptr);
+ JS_FreeValue(m_context, it.value());
+ it = m_artifactsScriptValues.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ });
+ JSValue obj = JS_NewObjectClass(context(), dataWithPtrClass());
+ attachPointerTo(obj, a);
+ setup(obj);
+ m_artifactsScriptValues.insert(qMakePair(a, moduleName), JS_DupValue(m_context, obj));
+ return obj;
+}
+
+void ScriptEngine::releaseInputArtifactScriptValues(const RuleNode *ruleNode)
+{
+ for (auto it = m_artifactsScriptValues.begin(); it != m_artifactsScriptValues.end();) {
+ Artifact * const a = it.key().first;
+ if (ruleNode->children.contains(a)) {
+ a->setDeregister({});
+ JS_FreeValue(m_context, it.value());
+ it = m_artifactsScriptValues.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
class JSTypeExtender
{
public:
- JSTypeExtender(ScriptEngine *engine, const QString &typeName)
- : m_engine(engine)
+ JSTypeExtender(ScriptEngine *engine, const QString &typeName) : m_engine(engine)
{
- m_proto = engine->globalObject().property(typeName)
- .property(QStringLiteral("prototype"));
- QBS_ASSERT(m_proto.isObject(), return);
- m_descriptor = engine->newObject();
+ const ScopedJsValue globalObject(engine->context(), JS_GetGlobalObject(engine->context()));
+ const ScopedJsValue type(engine->context(), getJsProperty(engine->context(),
+ globalObject, typeName));
+ m_proto = getJsProperty(engine->context(), type, QStringLiteral("prototype"));
+ QBS_ASSERT(JS_IsObject(m_proto), return);
+ }
+
+ ~JSTypeExtender()
+ {
+ JS_FreeValue(m_engine->context(), m_proto);
}
void addFunction(const QString &name, const QString &code)
{
- QScriptValue f = m_engine->evaluate(code);
- QBS_ASSERT(f.isFunction(), return);
- m_descriptor.setProperty(QStringLiteral("value"), f);
- m_engine->defineProperty(m_proto, name, m_descriptor);
+ const JSValue f = m_engine->evaluate(JsValueOwner::Caller, code);
+ QBS_ASSERT(JS_IsFunction(m_engine->context(), f), return);
+ JS_DefinePropertyValueStr(m_engine->context(), m_proto, name.toUtf8().constData(), f, 0);
}
private:
- ScriptEngine *const m_engine;
- QScriptValue m_proto;
- QScriptValue m_descriptor;
+ ScriptEngine * const m_engine;
+ JSValue m_proto = JS_UNDEFINED;
};
-static QScriptValue js_consoleError(QScriptContext *context, QScriptEngine *engine, Logger *logger)
+static JSValue js_consoleFunc(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv,
+ int level)
{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("console.error() expects 1 argument"));
- logger->qbsLog(LoggerError) << context->argument(0).toString();
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ QBS_ASSERT(engine, return JS_EXCEPTION);
+ if (Q_UNLIKELY(argc != 1))
+ return engine->throwError(Tr::tr("The console functions expect 1 argument."));
+ engine->logger().qbsLog(static_cast<LoggerLevel>(level)) << getJsString(ctx, argv[0]);
return engine->undefinedValue();
}
-static QScriptValue js_consoleWarn(QScriptContext *context, QScriptEngine *engine, Logger *logger)
-{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("console.warn() expects 1 argument"));
- logger->qbsWarning() << context->argument(0).toString();
- return engine->undefinedValue();
-}
-
-static QScriptValue js_consoleInfo(QScriptContext *context, QScriptEngine *engine, Logger *logger)
-{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("console.info() expects 1 argument"));
- logger->qbsInfo() << context->argument(0).toString();
- return engine->undefinedValue();
-}
-
-static QScriptValue js_consoleDebug(QScriptContext *context, QScriptEngine *engine, Logger *logger)
-{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("console.debug() expects 1 argument"));
- logger->qbsDebug() << context->argument(0).toString();
- return engine->undefinedValue();
-}
-
-static QScriptValue js_consoleLog(QScriptContext *context, QScriptEngine *engine, Logger *logger)
-{
- return js_consoleDebug(context, engine, logger);
-}
-
void ScriptEngine::installQbsBuiltins()
{
- globalObject().setProperty(StringConstants::qbsModule(), m_qbsObject = newObject());
-
- globalObject().setProperty(QStringLiteral("console"), m_consoleObject = newObject());
- installConsoleFunction(QStringLiteral("debug"), &js_consoleDebug);
- installConsoleFunction(QStringLiteral("error"), &js_consoleError);
- installConsoleFunction(QStringLiteral("info"), &js_consoleInfo);
- installConsoleFunction(QStringLiteral("log"), &js_consoleLog);
- installConsoleFunction(QStringLiteral("warn"), &js_consoleWarn);
+ const JSValue consoleObj = newObject();
+ setPropertyOnGlobalObject(QLatin1String("console"), consoleObj);
+ installConsoleFunction(consoleObj, QStringLiteral("debug"), LoggerDebug);
+ installConsoleFunction(consoleObj, QStringLiteral("error"), LoggerError);
+ installConsoleFunction(consoleObj, QStringLiteral("info"), LoggerInfo);
+ installConsoleFunction(consoleObj, QStringLiteral("log"), LoggerDebug);
+ installConsoleFunction(consoleObj, QStringLiteral("warn"), LoggerWarning);
}
void ScriptEngine::extendJavaScriptBuiltins()
@@ -774,48 +972,27 @@ void ScriptEngine::extendJavaScriptBuiltins()
JSTypeExtender stringExtender(this, QStringLiteral("String"));
stringExtender.addFunction(QStringLiteral("contains"),
QStringLiteral("(function(e){return this.indexOf(e) !== -1;})"));
- stringExtender.addFunction(QStringLiteral("startsWith"),
- QStringLiteral("(function(e){return this.slice(0, e.length) === e;})"));
- stringExtender.addFunction(QStringLiteral("endsWith"),
- QStringLiteral("(function(e){return this.slice(-e.length) === e;})"));
-}
-
-void ScriptEngine::installFunction(const QString &name, int length, QScriptValue *functionValue,
- FunctionSignature f, QScriptValue *targetObject = nullptr)
-{
- if (!functionValue->isValid())
- *functionValue = newFunction(f, length);
- (targetObject ? *targetObject : globalObject()).setProperty(name, *functionValue);
-}
-
-void ScriptEngine::installQbsFunction(const QString &name, int length, FunctionSignature f)
-{
- QScriptValue functionValue;
- installFunction(name, length, &functionValue, f, &m_qbsObject);
}
-void ScriptEngine::installConsoleFunction(const QString &name,
- QScriptValue (*f)(QScriptContext *, QScriptEngine *, Logger *))
+void ScriptEngine::installConsoleFunction(JSValue consoleObj, const QString &name,
+ LoggerLevel level)
{
- m_consoleObject.setProperty(name, newFunction(f, &m_logger));
+ JS_SetPropertyStr(m_context, consoleObj, name.toUtf8().constData(),
+ JS_NewCFunctionMagic(m_context, js_consoleFunc, name.toUtf8().constData(), 1,
+ JS_CFUNC_generic_magic, level));
}
-static QString loadFileString() { return QStringLiteral("loadFile"); }
-static QString loadExtensionString() { return QStringLiteral("loadExtension"); }
static QString requireString() { return QStringLiteral("require"); }
-void ScriptEngine::installImportFunctions()
+void ScriptEngine::installImportFunctions(JSValue importScope)
{
- installFunction(loadFileString(), 1, &m_loadFileFunction, js_loadFile);
- installFunction(loadExtensionString(), 1, &m_loadExtensionFunction, js_loadExtension);
- installFunction(requireString(), 1, &m_requireFunction, js_require);
+ const JSValue require = JS_NewCFunctionData(m_context, js_require, 1, 0, 1, &importScope);
+ setPropertyOnGlobalObject(requireString(), require);
}
void ScriptEngine::uninstallImportFunctions()
{
- globalObject().setProperty(loadFileString(), QScriptValue());
- globalObject().setProperty(loadExtensionString(), QScriptValue());
- globalObject().setProperty(requireString(), QScriptValue());
+ setPropertyOnGlobalObject(requireString(), JS_UNDEFINED);
}
ScriptEngine::PropertyCacheKey::PropertyCacheKey(QString moduleName,
@@ -826,5 +1003,25 @@ ScriptEngine::PropertyCacheKey::PropertyCacheKey(QString moduleName,
{
}
+JsException ScriptEngine::checkAndClearException(const CodeLocation &fallbackLocation) const
+{
+ return {m_context, JS_GetException(m_context), fallbackLocation};
+}
+
+void ScriptEngine::clearRequestedProperties()
+{
+ m_propertiesRequestedInScript.clear();
+ m_propertiesRequestedFromArtifact.clear();
+ m_importsRequestedInScript.clear();
+ m_productsWithRequestedDependencies.clear();
+ m_requestedArtifacts.clear();
+ m_requestedExports.clear();
+};
+
+void ScriptEngine::takeOwnership(JSValue v)
+{
+ ++m_evalResults[v];
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/scriptengine.h b/src/lib/corelib/language/scriptengine.h
index 68e346b68..c0a0948b7 100644
--- a/src/lib/corelib/language/scriptengine.h
+++ b/src/lib/corelib/language/scriptengine.h
@@ -45,9 +45,11 @@
#include <buildgraph/requestedartifacts.h>
#include <buildgraph/requesteddependencies.h>
#include <logging/logger.h>
+#include <quickjs.h>
#include <tools/codelocation.h>
#include <tools/filetime.h>
#include <tools/porting.h>
+#include <tools/scripttools.h>
#include <tools/set.h>
#include <QtCore/qdir.h>
@@ -56,8 +58,7 @@
#include <QtCore/qprocess.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptengine.h>
-
+#include <functional>
#include <memory>
#include <mutex>
#include <stack>
@@ -68,8 +69,10 @@
namespace qbs {
namespace Internal {
class Artifact;
+class Evaluator;
class JsImport;
class PrepareScriptObserver;
+class RuleNode;
class ScriptImporter;
class ScriptPropertyObserver;
@@ -86,35 +89,33 @@ public:
};
using DubiousContextList = std::vector<DubiousContext>;
-
-/*
- * ScriptObject that acquires resources, for example a file handle.
- * The ScriptObject should have QtOwnership and deleteLater() itself in releaseResources.
- */
-class ResourceAcquiringScriptObject
-{
-public:
- virtual ~ResourceAcquiringScriptObject() = default;
- virtual void releaseResources() = 0;
-};
+enum class JsValueOwner { Caller, ScriptEngine }; // TODO: This smells like cheating. Should always be Caller.
enum class ObserveMode { Enabled, Disabled };
-class QBS_AUTOTEST_EXPORT ScriptEngine : public QScriptEngine
+class QBS_AUTOTEST_EXPORT ScriptEngine
{
- Q_OBJECT
struct PrivateTag {};
public:
ScriptEngine(Logger &logger, EvalContext evalContext, PrivateTag);
- ~ScriptEngine() override;
+ ~ScriptEngine();
static std::unique_ptr<ScriptEngine> create(Logger &logger, EvalContext evalContext);
+ static ScriptEngine *engineForRuntime(const JSRuntime *runtime);
+ static ScriptEngine *engineForContext(const JSContext *ctx);
+ static LookupResult doExtraScopeLookup(JSContext *ctx, JSAtom prop);
+
+ void reset();
Logger &logger() const { return m_logger; }
- void import(const FileContextBaseConstPtr &fileCtx, QScriptValue &targetObject,
+ void import(const FileContextBaseConstPtr &fileCtx, JSValue &targetObject,
ObserveMode observeMode);
void clearImportsCache();
+ void registerEvaluator(Evaluator *evaluator);
+ void unregisterEvaluator(const Evaluator *evaluator);
+ Evaluator *evaluator() const { return m_evaluator; }
+
void setEvalContext(EvalContext c) { m_evalContext = c; }
EvalContext evalContext() const { return m_evalContext; }
void checkContext(const QString &operation, const DubiousContextList &dubiousContexts);
@@ -144,14 +145,7 @@ public:
}
void addPropertyRequestedFromArtifact(const Artifact *artifact, const Property &property);
void addRequestedExport(const ResolvedProduct *product) { m_requestedExports.insert(product); }
- void clearRequestedProperties() {
- m_propertiesRequestedInScript.clear();
- m_propertiesRequestedFromArtifact.clear();
- m_importsRequestedInScript.clear();
- m_productsWithRequestedDependencies.clear();
- m_requestedArtifacts.clear();
- m_requestedExports.clear();
- }
+ void clearRequestedProperties();
PropertySet propertiesRequestedInScript() const { return m_propertiesRequestedInScript; }
QHash<QString, PropertySet> propertiesRequestedFromArtifact() const {
return m_propertiesRequestedFromArtifact;
@@ -167,7 +161,7 @@ public:
RequestedArtifacts requestedArtifacts() const { return m_requestedArtifacts; }
Set<const ResolvedProduct *> requestedExports() const { return m_requestedExports; }
- void addImportRequestedInScript(qint64 importValueId);
+ void addImportRequestedInScript(quintptr importValueId);
std::vector<QString> importedFilesUsedInScript() const;
void setUsesIo() { m_usesIo = true; }
@@ -183,11 +177,8 @@ public:
QVariant retrieveFromPropertyCache(const QString &moduleName, const QString &propertyName,
const PropertyMapConstPtr &propertyMap);
- void defineProperty(QScriptValue &object, const QString &name, const QScriptValue &descriptor);
- void setObservedProperty(QScriptValue &object, const QString &name, const QScriptValue &value);
+ void setObservedProperty(JSValue &object, const QString &name, const JSValue &value);
void unobserveProperties();
- void setDeprecatedProperty(QScriptValue &object, const QString &name, const QString &newName,
- const QScriptValue &value);
PrepareScriptObserver *observer() const { return m_observer.get(); }
QProcessEnvironment environment() const;
@@ -206,23 +197,34 @@ public:
QHash<QString, FileTime> fileLastModifiedResults() const { return m_fileLastModifiedResult; }
Set<QString> imports() const;
- static QScriptValueList argumentList(const QStringList &argumentNames,
- const QScriptValue &context);
- QStringList uncaughtExceptionBacktraceOrEmpty() const {
- return hasUncaughtException() ? uncaughtExceptionBacktrace() : QStringList();
- }
- bool hasErrorOrException(const QScriptValue &v) const {
- return v.isError() || hasUncaughtException();
- }
- QScriptValue lastErrorValue(const QScriptValue &v) const {
- return v.isError() ? v : uncaughtException();
- }
- QString lastErrorString(const QScriptValue &v) const { return lastErrorValue(v).toString(); }
- CodeLocation lastErrorLocation(const QScriptValue &v,
- const CodeLocation &fallbackLocation = CodeLocation()) const;
- ErrorInfo lastError(const QScriptValue &v,
- const CodeLocation &fallbackLocation = CodeLocation()) const;
+ JSValue newObject() const;
+ JSValue newArray(int length, JsValueOwner owner);
+ void takeOwnership(JSValue v);
+ JSValue undefinedValue() const { return JS_UNDEFINED; }
+ JSValue toScriptValue(const QVariant &v) const { return makeJsVariant(m_context, v); }
+ JSValue evaluate(JsValueOwner resultOwner, const QString &code,
+ const QString &filePath = QString(), int line = 1,
+ const JSValueList &scopeChain = {});
+ void setLastLookupStatus(bool success) { m_lastLookupWasSuccess = success; }
+ JSContext *context() const { return m_context; }
+ JSValue globalObject() const { return m_globalObject; }
+ void setGlobalObject(JSValue obj) { m_globalObject = obj; }
+ void handleJsProperties(JSValueConst obj, const PropertyHandler &handler);
+ ScopedJsValueList argumentList(const QStringList &argumentNames, const JSValue &context) const;
+
+ using GetProperty = int (*)(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop);
+ using GetPropertyNames = int (*)(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj);
+ JSClassID registerClass(const char *name, JSClassCall *constructor, JSClassFinalizer *finalizer,
+ JSValue scope,
+ GetPropertyNames getPropertyNames = nullptr,
+ GetProperty getProperty = nullptr);
+ JSClassID getClassId(const char *name) const;
+
+ JsException checkAndClearException(const CodeLocation &fallbackLocation) const;
+ JSValue throwError(const QString &message) const;
void cancel();
@@ -231,77 +233,85 @@ public:
bool isActive() const { return m_active; }
void setActive(bool on) { m_active = on; }
- using QScriptEngine::newFunction;
-
- template <typename T, typename E,
- typename = std::enable_if_t<std::is_pointer_v<T>>,
- typename = std::enable_if_t<std::is_pointer_v<E>>,
- typename = std::enable_if_t<std::is_base_of_v<
- QScriptEngine, std::remove_pointer_t<E>>>
- > QScriptValue newFunction(QScriptValue (*signature)(QScriptContext *, E, T), T arg) {
- return QScriptEngine::newFunction(
- reinterpret_cast<FunctionWithArgSignature>(signature),
- reinterpret_cast<void *>(const_cast<
- std::add_pointer_t<
- std::remove_const_t<
- std::remove_pointer_t<T>>>>(arg)));
- }
-
- QScriptClass *modulePropertyScriptClass() const;
- void setModulePropertyScriptClass(QScriptClass *modulePropertyScriptClass);
+ JSClassID modulePropertyScriptClass() const;
+ void setModulePropertyScriptClass(JSClassID modulePropertyScriptClass);
- QScriptClass *productPropertyScriptClass() const { return m_productPropertyScriptClass; }
- void setProductPropertyScriptClass(QScriptClass *productPropertyScriptClass)
+ JSClassID productPropertyScriptClass() const { return m_productPropertyScriptClass; }
+ void setProductPropertyScriptClass(JSClassID productPropertyScriptClass)
{
m_productPropertyScriptClass = productPropertyScriptClass;
}
- QScriptClass *artifactsScriptClass() const { return m_artifactsScriptClass; }
- void setArtifactsScriptClass(QScriptClass *artifactsScriptClass)
+ JSClassID artifactsScriptClass(int index) const { return m_artifactsScriptClass[index]; }
+ void setArtifactsScriptClass(int index, JSClassID artifactsScriptClass)
{
- m_artifactsScriptClass = artifactsScriptClass;
+ m_artifactsScriptClass[index] = artifactsScriptClass;
}
- void addResourceAcquiringScriptObject(ResourceAcquiringScriptObject *obj);
- void releaseResourcesOfScriptObjects();
+ JSValue artifactsMapScriptValue(const ResolvedProduct *product);
+ void setArtifactsMapScriptValue(const ResolvedProduct *product, JSValue value);
+ JSValue artifactsMapScriptValue(const ResolvedModule *module);
+ void setArtifactsMapScriptValue(const ResolvedModule *module, JSValue value);
- QScriptValue &productScriptValuePrototype(const ResolvedProduct *product)
+ JSValue getArtifactProperty(JSValue obj,
+ const std::function<JSValue(const Artifact *)> &propGetter);
+
+ JSValue& baseProductScriptValue(const ResolvedProduct *product)
{
- return m_productScriptValues[product];
+ return m_baseProductScriptValues[product];
}
- QScriptValue &projectScriptValue(const ResolvedProject *project)
+ JSValue &projectScriptValue(const ResolvedProject *project)
{
return m_projectScriptValues[project];
}
- QScriptValue &moduleScriptValuePrototype(const ResolvedModule *module)
+ JSValue &baseModuleScriptValue(const ResolvedModule *module)
{
- return m_moduleScriptValues[module];
+ return m_baseModuleScriptValues[module];
}
-private:
- QScriptValue newFunction(FunctionWithArgSignature signature, void *arg) Q_DECL_EQ_DELETE;
+ JSValue getArtifactScriptValue(Artifact *a, const QString &moduleName,
+ const std::function<void(JSValue obj)> &setup);
+ void releaseInputArtifactScriptValues(const RuleNode *ruleNode);
- void abort();
+ const JSValueList &contextStack() const { return m_contextStack; }
+
+ JSClassID dataWithPtrClass() const { return m_dataWithPtrClass; }
+
+ JSValue getInternalExtension(const char *name) const;
+ void addInternalExtension(const char *name, JSValue ext);
+ JSValue asJsValue(const QString &s);
+ JSValue asJsValue(const QStringList &l);
+ JSValue asJsValue(const QVariantList &l);
+ JSValue asJsValue(const QVariantMap &m);
+
+ QVariant property(const char *name) const { return m_properties.value(QLatin1String(name)); }
+ void setProperty(const char *k, const QVariant &v) { m_properties.insert(QLatin1String(k), v); }
+
+private:
+ static int interruptor(JSRuntime *rt, void *opaqueEngine);
bool gatherFileResults() const;
+ void setMaxStackSize();
+ void setPropertyOnGlobalObject(const QString &property, JSValue value);
void installQbsBuiltins();
void extendJavaScriptBuiltins();
- void installFunction(const QString &name, int length, QScriptValue *functionValue,
- FunctionSignature f, QScriptValue *targetObject);
- void installQbsFunction(const QString &name, int length, FunctionSignature f);
- void installConsoleFunction(const QString &name,
- QScriptValue (*f)(QScriptContext *, QScriptEngine *, Logger *));
- void installImportFunctions();
+ void installConsoleFunction(JSValue consoleObj, const QString &name, LoggerLevel level);
+ void installImportFunctions(JSValue importScope);
void uninstallImportFunctions();
- void import(const JsImport &jsImport, QScriptValue &targetObject);
- void observeImport(QScriptValue &jsImport);
- void importFile(const QString &filePath, QScriptValue &targetObject);
- static QScriptValue js_loadExtension(QScriptContext *context, QScriptEngine *qtengine);
- static QScriptValue js_loadFile(QScriptContext *context, QScriptEngine *qtengine);
- static QScriptValue js_require(QScriptContext *context, QScriptEngine *qtengine);
+ void import(const JsImport &jsImport, JSValue &targetObject);
+ void observeImport(JSValue &jsImport);
+ void importFile(const QString &filePath, JSValue &targetObject);
+ static JSValue js_require(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic, JSValue *func_data);
+ JSValue mergeExtensionObjects(const JSValueList &lst);
+ JSValue loadInternalExtension(const QString &uri);
+
+ static void handleUndefinedFound(JSContext *ctx);
+ static void handleFunctionEntered(JSContext *ctx, JSValue this_obj);
+ static void handleFunctionExited(JSContext *ctx);
class PropertyCacheKey
{
@@ -320,21 +330,24 @@ private:
friend bool operator==(const PropertyCacheKey &lhs, const PropertyCacheKey &rhs);
friend QHashValueType qHash(const ScriptEngine::PropertyCacheKey &k, QHashValueType seed);
- static std::mutex m_creationDestructionMutex;
+ JSRuntime * const m_jsRuntime = JS_NewRuntime();
+ JSContext * const m_context = JS_NewContext(m_jsRuntime);
+ JSValue m_globalObject = JS_NULL;
ScriptImporter *m_scriptImporter;
- QScriptClass *m_modulePropertyScriptClass;
- QScriptClass *m_productPropertyScriptClass = nullptr;
- QScriptClass *m_artifactsScriptClass = nullptr;
- QHash<JsImport, QScriptValue> m_jsImportCache;
- std::unordered_map<QString, QScriptValue> m_jsFileCache;
- bool m_propertyCacheEnabled;
- bool m_active;
+ JSClassID m_modulePropertyScriptClass = 0;
+ JSClassID m_productPropertyScriptClass = 0;
+ JSClassID m_artifactsScriptClass[2] = {0, 0};
+ JSClassID m_dataWithPtrClass = 0;
+ Evaluator *m_evaluator = nullptr;
+ QHash<JsImport, JSValue> m_jsImportCache;
+ std::unordered_map<QString, JSValue> m_jsFileCache;
+ bool m_propertyCacheEnabled = true;
+ bool m_active = false;
+ bool m_canceling = false;
QHash<PropertyCacheKey, QVariant> m_propertyCache;
PropertySet m_propertiesRequestedInScript;
QHash<QString, PropertySet> m_propertiesRequestedFromArtifact;
Logger &m_logger;
- QScriptValue m_definePropertyFunction;
- QScriptValue m_emptyFunction;
QProcessEnvironment m_environment;
QHash<QString, QString> m_canonicalFilePathResult;
QHash<QString, bool> m_fileExistsResult;
@@ -342,28 +355,36 @@ private:
QHash<QString, FileTime> m_fileLastModifiedResult;
std::stack<QString> m_currentDirPathStack;
std::stack<QStringList> m_extensionSearchPathsStack;
- QScriptValue m_loadFileFunction;
- QScriptValue m_loadExtensionFunction;
- QScriptValue m_requireFunction;
- QScriptValue m_qbsObject;
- QScriptValue m_consoleObject;
- QScriptValue m_cancelationError;
+ std::stack<std::pair<QString, int>> m_evalPositions;
+ JSValue m_qbsObject = JS_UNDEFINED;
qint64 m_elapsedTimeImporting = -1;
bool m_usesIo = false;
EvalContext m_evalContext;
- std::vector<ResourceAcquiringScriptObject *> m_resourceAcquiringScriptObjects;
const std::unique_ptr<PrepareScriptObserver> m_observer;
- std::vector<std::tuple<QScriptValue, QString, QScriptValue>> m_observedProperties;
- std::vector<QScriptValue> m_requireResults;
- std::unordered_map<qint64, std::vector<QString>> m_filePathsPerImport;
+ std::vector<std::tuple<JSValue, QString, JSValue>> m_observedProperties;
+ JSValueList m_requireResults;
+ std::unordered_map<quintptr, std::vector<QString>> m_filePathsPerImport;
std::vector<qint64> m_importsRequestedInScript;
Set<const ResolvedProduct *> m_productsWithRequestedDependencies;
RequestedArtifacts m_requestedArtifacts;
Set<const ResolvedProduct *> m_requestedExports;
ObserveMode m_observeMode = ObserveMode::Disabled;
- std::unordered_map<const ResolvedProduct *, QScriptValue> m_productScriptValues;
- std::unordered_map<const ResolvedProject *, QScriptValue> m_projectScriptValues;
- std::unordered_map<const ResolvedModule *, QScriptValue> m_moduleScriptValues;
+ std::unordered_map<const ResolvedProduct *, JSValue> m_baseProductScriptValues;
+ std::unordered_map<const ResolvedProduct *, JSValue> m_productArtifactsMapScriptValues;
+ std::unordered_map<const ResolvedModule *, JSValue> m_moduleArtifactsMapScriptValues;
+ std::unordered_map<const ResolvedProject *, JSValue> m_projectScriptValues;
+ std::unordered_map<const ResolvedModule *, JSValue> m_baseModuleScriptValues;
+ QList<JSValueList> m_scopeChains;
+ JSValueList m_contextStack;
+ QHash<JSClassID, JSClassExoticMethods> m_exoticMethods;
+ QHash<QString, JSClassID> m_classes;
+ QHash<QString, JSValue> m_internalExtensions;
+ QHash<QString, JSValue> m_stringCache;
+ QHash<JSValue, int> m_evalResults;
+ QHash<QPair<Artifact *, QString>, JSValue> m_artifactsScriptValues;
+ QVariantMap m_properties;
+ std::recursive_mutex m_artifactsMutex;
+ bool m_lastLookupWasSuccess = false;
};
class EvalContextSwitcher
diff --git a/src/lib/corelib/language/scriptimporter.cpp b/src/lib/corelib/language/scriptimporter.cpp
index 40162eb12..1b012f3c3 100644
--- a/src/lib/corelib/language/scriptimporter.cpp
+++ b/src/lib/corelib/language/scriptimporter.cpp
@@ -48,8 +48,6 @@
#include <parser/qmljsparser_p.h>
#include <tools/error.h>
-#include <QtScript/qscriptvalueiterator.h>
-
namespace qbs {
namespace Internal {
@@ -121,10 +119,10 @@ ScriptImporter::ScriptImporter(ScriptEngine *scriptEngine)
{
}
-QScriptValue ScriptImporter::importSourceCode(const QString &sourceCode, const QString &filePath,
- QScriptValue &targetObject)
+JSValue ScriptImporter::importSourceCode(const QString &sourceCode, const QString &filePath,
+ JSValue &targetObject)
{
- Q_ASSERT(targetObject.isObject());
+ Q_ASSERT(JS_IsObject(targetObject));
// The targetObject doesn't get overwritten but enhanced by the contents of the .js file.
// This is necessary for library imports that consist of multiple js files.
@@ -144,19 +142,17 @@ QScriptValue ScriptImporter::importSourceCode(const QString &sourceCode, const Q
code = QLatin1String("(function(){\n") + sourceCode + extractor.suffix();
}
- QScriptValue result = m_engine->evaluate(code, filePath, 0);
- throwOnEvaluationError(m_engine, result, [&filePath] () { return CodeLocation(filePath, 0); });
- copyProperties(result, targetObject);
+ JSValue result = m_engine->evaluate(JsValueOwner::Caller, code, filePath, 0);
+ throwOnEvaluationError(m_engine, [&filePath] () { return CodeLocation(filePath, 0); });
+ copyProperties(m_engine->context(), result, targetObject);
return result;
}
-void ScriptImporter::copyProperties(const QScriptValue &src, QScriptValue &dst)
+void ScriptImporter::copyProperties(JSContext *ctx, const JSValue &src, JSValue &dst)
{
- QScriptValueIterator it(src);
- while (it.hasNext()) {
- it.next();
- dst.setProperty(it.name(), it.value());
- }
+ handleJsProperties(ctx, src, [ctx, &dst](const JSAtom &name, const JSPropertyDescriptor &desc) {
+ JS_SetProperty(ctx, dst, name, JS_DupValue(ctx, desc.value));
+ });
}
} // namespace Internal
diff --git a/src/lib/corelib/language/scriptimporter.h b/src/lib/corelib/language/scriptimporter.h
index 8cff09382..6bec9b088 100644
--- a/src/lib/corelib/language/scriptimporter.h
+++ b/src/lib/corelib/language/scriptimporter.h
@@ -40,9 +40,9 @@
#ifndef SCRIPTIMPORTER_H
#define SCRIPTIMPORTER_H
-#include <QtCore/qhash.h>
+#include <quickjs.h>
-#include <QtScript/qscriptvalue.h>
+#include <QtCore/qhash.h>
namespace qbs {
namespace Internal {
@@ -53,9 +53,10 @@ class ScriptImporter
{
public:
ScriptImporter(ScriptEngine *scriptEngine);
- QScriptValue importSourceCode(const QString &sourceCode, const QString &filePath, QScriptValue &targetObject);
+ JSValue importSourceCode(const QString &sourceCode, const QString &filePath,
+ JSValue &targetObject);
- static void copyProperties(const QScriptValue &src, QScriptValue &dst);
+ static void copyProperties(JSContext *ctx, const JSValue &src, JSValue &dst);
private:
ScriptEngine *m_engine;
diff --git a/src/lib/corelib/language/scriptpropertyobserver.h b/src/lib/corelib/language/scriptpropertyobserver.h
index 7fb362b95..80da705ee 100644
--- a/src/lib/corelib/language/scriptpropertyobserver.h
+++ b/src/lib/corelib/language/scriptpropertyobserver.h
@@ -40,10 +40,11 @@
#ifndef QBS_SCRIPTPROPERTYOBSERVER_H
#define QBS_SCRIPTPROPERTYOBSERVER_H
+#include <quickjs.h>
+
#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
-class QScriptValue;
class QString;
QT_END_NAMESPACE
@@ -64,8 +65,8 @@ public:
virtual ~ScriptPropertyObserver();
- virtual void onPropertyRead(const QScriptValue &object, const QString &name,
- const QScriptValue &value) = 0;
+ virtual void onPropertyRead(const JSValue &object, const QString &name,
+ const JSValue &value) = 0;
protected:
ScriptEngine * engine() const { return m_engine; }
diff --git a/src/lib/corelib/tools/codelocation.cpp b/src/lib/corelib/tools/codelocation.cpp
index ebfd5edc6..ea77fee9d 100644
--- a/src/lib/corelib/tools/codelocation.cpp
+++ b/src/lib/corelib/tools/codelocation.cpp
@@ -87,8 +87,9 @@ CodeLocation::CodeLocation(const QString &aFilePath, int aLine, int aColumn, boo
}
CodeLocation::CodeLocation(const CodeLocation &other) = default;
-
+CodeLocation::CodeLocation(CodeLocation &&other) noexcept = default;
CodeLocation &CodeLocation::operator=(const CodeLocation &other) = default;
+CodeLocation &CodeLocation::operator=(CodeLocation &&other) noexcept = default;
CodeLocation::~CodeLocation() = default;
diff --git a/src/lib/corelib/tools/codelocation.h b/src/lib/corelib/tools/codelocation.h
index 158b90766..33b832df0 100644
--- a/src/lib/corelib/tools/codelocation.h
+++ b/src/lib/corelib/tools/codelocation.h
@@ -62,7 +62,9 @@ public:
explicit CodeLocation(const QString &aFilePath, int aLine = -1, int aColumn = -1,
bool checkPath = true);
CodeLocation(const CodeLocation &other);
+ CodeLocation(CodeLocation &&other) noexcept;
CodeLocation &operator=(const CodeLocation &other);
+ CodeLocation &operator=(CodeLocation &&other) noexcept;
~CodeLocation();
QString filePath() const;
diff --git a/src/lib/corelib/tools/error.cpp b/src/lib/corelib/tools/error.cpp
index 2ff36d1d1..16ce47ca9 100644
--- a/src/lib/corelib/tools/error.cpp
+++ b/src/lib/corelib/tools/error.cpp
@@ -40,7 +40,6 @@
#include "error.h"
#include "persistence.h"
-#include "qttools.h"
#include "stringconstants.h"
#include "setupprojectparameters.h"
#include "logging/logger.h"
@@ -209,15 +208,21 @@ ErrorInfo::ErrorInfo(const QString &description, const QStringList &backtrace)
{
append(description);
for (const QString &traceLine : backtrace) {
- static const std::regex regexp("^(.+) at (.+):(\\-?[0-9]+)$");
+ if (traceLine.contains(QStringLiteral("<eval>")))
+ continue;
+ static const std::regex regexpWithFunc("^(.+) at [^(]*\\((.+):(\\-?[0-9]+)\\)$");
+ static const std::regex regexpWithoutFunc("^(.+) at (.+):(\\-?[0-9]+)$");
std::smatch match;
const std::string tl = traceLine.toStdString();
- if (std::regex_match(tl, match, regexp)) {
- const QString message = QString::fromStdString(match[1]),
- file = QString::fromStdString(match[2]),
- line = QString::fromStdString(match[3]);
+ bool hasMatch = std::regex_match(tl, match, regexpWithFunc);
+ if (!hasMatch)
+ hasMatch = std::regex_match(tl, match, regexpWithoutFunc);
+ if (hasMatch) {
+ const QString message = QString::fromStdString(match[1]).trimmed();
+ const QString file = QString::fromStdString(match[2]);
+ const QString line = QString::fromStdString(match[3]);
const CodeLocation location(file, line.toInt());
- appendBacktrace(message, location);
+ appendBacktrace(message, CodeLocation(file, line.toInt()));
}
}
}
diff --git a/src/lib/corelib/tools/progressobserver.h b/src/lib/corelib/tools/progressobserver.h
index fc49d9eed..9acb4b30c 100644
--- a/src/lib/corelib/tools/progressobserver.h
+++ b/src/lib/corelib/tools/progressobserver.h
@@ -47,6 +47,7 @@ QT_END_NAMESPACE
namespace qbs {
namespace Internal {
+class ScriptEngine;
class ProgressObserver
{
@@ -64,6 +65,14 @@ public:
// Call this to ensure that the progress bar always goes to 100%.
void setFinished();
+
+ void setScriptEngine(ScriptEngine *engine) { m_scriptEngine = engine; }
+
+protected:
+ ScriptEngine *scriptEngine() const { return m_scriptEngine; }
+
+private:
+ ScriptEngine *m_scriptEngine = nullptr;
};
} // namespace Internal
diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp
index ed88520fc..ea0b239fa 100644
--- a/src/lib/corelib/tools/scripttools.cpp
+++ b/src/lib/corelib/tools/scripttools.cpp
@@ -39,9 +39,10 @@
#include "scripttools.h"
-#include <QtCore/qdatastream.h>
+#include <language/scriptengine.h>
+#include <tools/error.h>
-#include <QtScript/qscriptengine.h>
+#include <QtCore/qdatastream.h>
namespace qbs {
namespace Internal {
@@ -65,16 +66,246 @@ QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name)
return getConfigProperty(cfg.value(name.front()).toMap(), name.mid(1));
}
-TemporaryGlobalObjectSetter::TemporaryGlobalObjectSetter(const QScriptValue &object)
+TemporaryGlobalObjectSetter::TemporaryGlobalObjectSetter(
+ ScriptEngine *engine, const JSValue &object)
+ : m_engine(engine), m_oldGlobalObject(engine->globalObject())
{
- QScriptEngine *engine = object.engine();
- m_oldGlobalObject = engine->globalObject();
engine->setGlobalObject(object);
}
TemporaryGlobalObjectSetter::~TemporaryGlobalObjectSetter()
{
- m_oldGlobalObject.engine()->setGlobalObject(m_oldGlobalObject);
+ m_engine->setGlobalObject(m_oldGlobalObject);
+}
+
+JsException::JsException(JsException &&other) noexcept
+ : m_ctx(other.m_ctx), m_exception(other.m_exception),
+ m_fallbackLocation(std::move(other.m_fallbackLocation))
+{
+ other.m_exception = JS_NULL;
+}
+
+JsException::~JsException() { JS_FreeValue(m_ctx, m_exception); }
+
+QString JsException::message() const
+{
+ if (JS_IsString(m_exception))
+ return getJsString(m_ctx, m_exception);
+ return getJsStringProperty(m_ctx, m_exception, QStringLiteral("message"));
+}
+
+const QStringList JsException::stackTrace() const
+{
+ return getJsStringProperty(m_ctx, m_exception, QLatin1String("stack"))
+ .split(QLatin1Char('\n'), Qt::SkipEmptyParts);
+}
+
+ErrorInfo JsException::toErrorInfo() const
+{
+ const QString msg = message();
+ ErrorInfo e(msg, stackTrace());
+ if (e.hasLocation() || !m_fallbackLocation.isValid())
+ return e;
+ return ErrorInfo(msg, m_fallbackLocation);
+}
+
+void defineJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val)
+{
+ JS_DefinePropertyValueStr(ctx, obj, prop.toUtf8().constData(), val, 0);
+}
+
+JSValue getJsProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ return JS_GetPropertyStr(ctx, obj, prop.toUtf8().constData());
+}
+
+void setJsProperty(JSContext *ctx, JSValue obj, const QString &prop, JSValue val)
+{
+ if (JS_SetPropertyStr(ctx, obj, prop.toUtf8().constData(), val) <= 0)
+ qDebug() << "Oje!";
+}
+
+void setJsProperty(JSContext *ctx, JSValue obj, const QString &prop, const QString &val)
+{
+ setJsProperty(ctx, obj, prop, makeJsString(ctx, val));
+}
+
+void handleJsProperties(JSContext *ctx, JSValue obj,
+ const std::function<void (const JSAtom &,
+ const JSPropertyDescriptor &)> &handler)
+{
+ struct PropsHolder {
+ PropsHolder(JSContext *ctx) : ctx(ctx) {}
+ ~PropsHolder() {
+ for (uint32_t i = 0; i < count; ++i)
+ JS_FreeAtom(ctx, props[i].atom);
+ js_free(ctx, props);
+ }
+ JSContext * const ctx;
+ JSPropertyEnum *props = nullptr;
+ uint32_t count = 0;
+ } propsHolder(ctx);
+ JS_GetOwnPropertyNames(ctx, &propsHolder.props, &propsHolder.count, obj, JS_GPN_STRING_MASK);
+ for (uint32_t i = 0; i < propsHolder.count; ++i) {
+ JSPropertyDescriptor desc{0, JS_UNDEFINED, JS_UNDEFINED, JS_UNDEFINED};
+ JS_GetOwnProperty(ctx, &desc, obj, propsHolder.props[i].atom);
+ const ScopedJsValueList valueHolder(ctx, {desc.value, desc.getter, desc.setter});
+ handler(propsHolder.props[i].atom, desc);
+ }
+}
+
+QString getJsString(JSContext *ctx, JSValueConst val)
+{
+ size_t strLen;
+ const char * const str = JS_ToCStringLen(ctx, &strLen, val);
+ QString s = QString::fromUtf8(str, strLen);
+ JS_FreeCString(ctx, str);
+ return s;
+}
+
+QString getJsStringProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ const ScopedJsValue sv(ctx, getJsProperty(ctx, obj, prop));
+ return getJsString(ctx, sv);
+}
+
+int getJsIntProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ return JS_VALUE_GET_INT(getJsProperty(ctx, obj, prop));
+}
+
+bool getJsBoolProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ return JS_VALUE_GET_BOOL(getJsProperty(ctx, obj, prop));
+}
+
+JSValue makeJsString(JSContext *ctx, const QString &s)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(s);
+}
+
+JSValue makeJsStringList(JSContext *ctx, const QStringList &l)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(l);
+}
+
+JSValue throwError(JSContext *ctx, const QString &message)
+{
+ return JS_Throw(ctx, makeJsString(ctx, message));
+}
+
+QStringList getJsStringList(JSContext *ctx, JSValue val)
+{
+ if (JS_IsString(val))
+ return {getJsString(ctx, val)};
+ if (!JS_IsArray(ctx, val))
+ return {};
+ const int size = getJsIntProperty(ctx, val, QLatin1String("length"));
+ QStringList l;
+ for (int i = 0; i < size; ++i) {
+ const ScopedJsValue elem(ctx, JS_GetPropertyUint32(ctx, val, i));
+ l << getJsString(ctx, elem);
+ }
+ return l;
+}
+
+JSValue makeJsVariant(JSContext *ctx, const QVariant &v)
+{
+ switch (static_cast<QMetaType::Type>(v.userType())) {
+ case QMetaType::QString:
+ return makeJsString(ctx, v.toString());
+ case QMetaType::QStringList:
+ return makeJsStringList(ctx, v.toStringList());
+ case QMetaType::QVariantList:
+ return makeJsVariantList(ctx, v.toList());
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ return JS_NewInt32(ctx, v.toInt());
+ case QMetaType::Long:
+ case QMetaType::ULong:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ return JS_NewInt64(ctx, v.toInt());
+ case QMetaType::Bool:
+ return JS_NewBool(ctx, v.toBool());
+ case QMetaType::QVariantMap:
+ return makeJsVariantMap(ctx, v.toMap());
+ default:
+ return JS_UNDEFINED;
+ }
+}
+
+JSValue makeJsVariantList(JSContext *ctx, const QVariantList &l)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(l);
+}
+
+JSValue makeJsVariantMap(JSContext *ctx, const QVariantMap &m)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(m);
+}
+
+static QVariant getJsVariantImpl(JSContext *ctx, JSValue val, QList<JSValue> path)
+{
+ if (JS_IsString(val))
+ return getJsString(ctx, val);
+ if (JS_IsBool(val))
+ return bool(JS_VALUE_GET_BOOL(val));
+ if (JS_IsArray(ctx, val)) {
+ if (path.contains(val))
+ return {};
+ path << val;
+ QVariantList l;
+ const int size = getJsIntProperty(ctx, val, QLatin1String("length"));
+ for (int i = 0; i < size; ++i) {
+ const ScopedJsValue sv(ctx, JS_GetPropertyUint32(ctx, val, i));
+ l << getJsVariantImpl(ctx, sv, path);
+ }
+ return l;
+ }
+ if (JS_IsObject(val)) {
+ if (path.contains(val))
+ return {};
+ path << val;
+ QVariantMap map;
+ handleJsProperties(ctx, val, [ctx, &map, path](const JSAtom &prop, const JSPropertyDescriptor &desc) {
+ map.insert(getJsString(ctx, prop), getJsVariantImpl(ctx, desc.value, path));
+ });
+ return map;
+ }
+ const auto tag = JS_VALUE_GET_TAG(val);
+ if (tag == JS_TAG_INT)
+ return JS_VALUE_GET_INT(val);
+ else if (tag == JS_TAG_FLOAT64)
+ return JS_VALUE_GET_FLOAT64(val);
+ return {};
+}
+
+QVariant getJsVariant(JSContext *ctx, JSValue val)
+{
+ return getJsVariantImpl(ctx, val, {});
+}
+
+QStringList getJsStringListProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ JSValue array = getJsProperty(ctx, obj, prop);
+ QStringList list = getJsStringList(ctx, array);
+ JS_FreeValue(ctx, array);
+ return list;
+}
+
+QVariant getJsVariantProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ const JSValue vv = getJsProperty(ctx, obj, prop);
+ QVariant v = getJsVariant(ctx, vv);
+ JS_FreeValue(ctx, vv);
+ return v;
+}
+
+QString getJsString(JSContext *ctx, JSAtom atom)
+{
+ const ScopedJsValue atomString(ctx, JS_AtomToString(ctx, atom));
+ return getJsString(ctx, atomString);
}
} // namespace Internal
diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h
index 4a258b98f..1af7b2b9e 100644
--- a/src/lib/corelib/tools/scripttools.h
+++ b/src/lib/corelib/tools/scripttools.h
@@ -40,57 +40,169 @@
#ifndef QBS_SCRIPTTOOLS_H
#define QBS_SCRIPTTOOLS_H
-#include <tools/qbs_export.h>
+#include "codelocation.h"
+#include "porting.h"
+#include "qbs_export.h"
+#include <quickjs.h>
+
+#include <QtCore/qhash.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptprogram.h>
-#include <QtScript/qscriptvalue.h>
+#include <utility>
+#include <vector>
namespace qbs {
+class ErrorInfo;
namespace Internal {
+class ScriptEngine;
-template <typename C>
-QScriptValue toScriptValue(QScriptEngine *scriptEngine, const C &container)
-{
- QScriptValue v = scriptEngine->newArray(container.size());
- int i = 0;
- for (const typename C::value_type &item : container)
- v.setProperty(i++, scriptEngine->toScriptValue(item));
- return v;
-}
+using JSValueList = std::vector<JSValue>;
-void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value);
-QVariant QBS_AUTOTEST_EXPORT getConfigProperty(const QVariantMap &cfg, const QStringList &name);
+void defineJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val);
+JSValue getJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+void setJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val);
+void setJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, const QString &val);
+QString getJsStringProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+QStringList getJsStringListProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+int getJsIntProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+bool getJsBoolProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+QVariant getJsVariantProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+QString getJsString(JSContext *ctx, JSValueConst val);
+QString getJsString(JSContext *ctx, JSAtom atom);
+QBS_AUTOTEST_EXPORT QVariant getJsVariant(JSContext *ctx, JSValueConst val);
+JSValue makeJsString(JSContext *ctx, const QString &s);
+JSValue makeJsStringList(JSContext *ctx, const QStringList &l);
+JSValue makeJsVariant(JSContext *ctx, const QVariant &v);
+JSValue makeJsVariantList(JSContext *ctx, const QVariantList &l);
+JSValue makeJsVariantMap(JSContext *ctx, const QVariantMap &m);
+QStringList getJsStringList(JSContext *ctx, JSValueConst val);
+JSValue throwError(JSContext *ctx, const QString &message);
+using PropertyHandler = std::function<void(const JSAtom &, const JSPropertyDescriptor &)>;
+void handleJsProperties(JSContext *ctx, JSValueConst obj, const PropertyHandler &handler);
+inline quintptr jsObjectId(const JSValue &val) { return quintptr(JS_VALUE_GET_OBJ(val)); }
template <class T>
-void attachPointerTo(QScriptValue &scriptValue, T *ptr)
+void attachPointerTo(JSValue &scriptValue, T *ptr)
{
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(ptr));
- scriptValue.setData(scriptValue.engine()->newVariant(v));
+ JS_SetOpaque(scriptValue, const_cast<std::remove_const_t<T> *>(ptr));
}
template <class T>
-T *attachedPointer(const QScriptValue &scriptValue)
+T *attachedPointer(const JSValue &scriptValue, JSClassID id)
{
- const auto ptr = scriptValue.data().toVariant().value<quintptr>();
- return reinterpret_cast<T *>(ptr);
+ return reinterpret_cast<T *>(JS_GetOpaque(scriptValue, id));
}
class TemporaryGlobalObjectSetter
{
public:
- TemporaryGlobalObjectSetter(const QScriptValue &object);
+ TemporaryGlobalObjectSetter(ScriptEngine *engine, const JSValue &object);
~TemporaryGlobalObjectSetter();
private:
- QScriptValue m_oldGlobalObject;
+ ScriptEngine * const m_engine;
+ const JSValue m_oldGlobalObject;
+};
+
+class ScopedJsValue
+{
+public:
+ ScopedJsValue(JSContext *ctx, JSValue v) : m_context(ctx), m_value(v) {}
+ void setValue(JSValue v) { JS_FreeValue(m_context, m_value); m_value = v; }
+ ~ScopedJsValue() { JS_FreeValue(m_context, m_value); }
+ operator JSValue() const { return m_value; }
+ JSValue release() { const JSValue v = m_value; m_value = JS_UNDEFINED; return v; }
+ void reset(JSValue v) { JS_FreeValue(m_context, m_value); m_value = v; }
+
+ ScopedJsValue(const ScopedJsValue &) = delete;
+ ScopedJsValue &operator=(const ScopedJsValue &) = delete;
+
+ ScopedJsValue(ScopedJsValue && other) : m_context(other.m_context), m_value(other.m_value)
+ {
+ other.m_value = JS_UNDEFINED;
+ }
+
+private:
+ JSContext * const m_context;
+ JSValue m_value;
};
+class ScopedJsValueList
+{
+public:
+ ScopedJsValueList(JSContext *ctx, const JSValueList &l) : m_context(ctx), m_list(l) {}
+ ~ScopedJsValueList() { for (const JSValue v : m_list) JS_FreeValue(m_context, v); }
+ operator JSValueList() const { return m_list; }
+
+ ScopedJsValueList(const ScopedJsValueList &) = delete;
+ ScopedJsValueList &operator=(const ScopedJsValueList &) = delete;
+
+ ScopedJsValueList(ScopedJsValueList && other) noexcept
+ : m_context(other.m_context), m_list(std::move(other.m_list))
+ {
+ other.m_list.clear();
+ }
+
+private:
+ JSContext * const m_context;
+ JSValueList m_list;
+};
+
+class ScopedJsAtom
+{
+public:
+ ScopedJsAtom(JSContext *ctx, JSAtom a) : m_context(ctx), m_atom(a) {}
+ ScopedJsAtom(JSContext *ctx, const QByteArray &s)
+ : ScopedJsAtom(ctx, JS_NewAtom(ctx, s.constData())) {}
+ ScopedJsAtom(JSContext *ctx, const QString &s) : ScopedJsAtom(ctx, s.toUtf8()) {}
+ ~ScopedJsAtom() { JS_FreeAtom(m_context, m_atom); }
+ operator JSAtom() const { return m_atom; }
+
+ ScopedJsAtom(const ScopedJsAtom &) = delete;
+ ScopedJsAtom&operator=(const ScopedJsAtom &) = delete;
+
+ ScopedJsAtom(ScopedJsAtom &&other) : m_context(other.m_context), m_atom(other.m_atom)
+ {
+ other.m_atom = 0;
+ }
+
+private:
+ JSContext * const m_context;
+ JSAtom m_atom;
+};
+
+class QBS_AUTOTEST_EXPORT JsException
+{
+public:
+ JsException(JSContext *ctx, JSValue ex, const CodeLocation &fallbackLocation)
+ : m_ctx(ctx), m_exception(ex), m_fallbackLocation(fallbackLocation) {}
+ JsException(JsException && other) noexcept;
+ ~JsException();
+ JsException(const JsException &) = delete;
+ JsException &operator=(const JsException &) = delete;
+
+ operator bool() const { return !JS_IsNull(m_exception); }
+ QString message() const;
+ const QStringList stackTrace() const;
+ ErrorInfo toErrorInfo() const;
+private:
+ JSContext *m_ctx;
+ JSValue m_exception;
+ CodeLocation m_fallbackLocation;
+};
+
+void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value);
+QVariant QBS_AUTOTEST_EXPORT getConfigProperty(const QVariantMap &cfg, const QStringList &name);
+
} // namespace Internal
} // namespace qbs
+// Only to be used for objects!
+inline bool operator==(JSValue v1, JSValue v2) { return v1.u.ptr == v2.u.ptr; }
+inline bool operator!=(JSValue v1, JSValue v2) { return !(v1 == v2); }
+inline bool operator<(JSValue v1, JSValue v2) { return v1.u.ptr < v2.u.ptr; }
+inline qbs::QHashValueType qHash(const JSValue &v) { return QT_PREPEND_NAMESPACE(qHash)(v.u.ptr); }
+
#endif // QBS_SCRIPTTOOLS_H
diff --git a/src/lib/corelib/tools/settingsrepresentation.cpp b/src/lib/corelib/tools/settingsrepresentation.cpp
index 7790f1aa1..6dfd81beb 100644
--- a/src/lib/corelib/tools/settingsrepresentation.cpp
+++ b/src/lib/corelib/tools/settingsrepresentation.cpp
@@ -41,8 +41,8 @@
#include "jsliterals.h"
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
namespace qbs {
@@ -54,11 +54,19 @@ QString settingsValueToRepresentation(const QVariant &value)
static QVariant variantFromString(const QString &str, bool &ok)
{
// ### use Qt5's JSON reader at some point.
- QScriptEngine engine;
- QScriptValue sv = engine.evaluate(QLatin1String("(function(){return ")
- + str + QLatin1String(";})()"));
- ok = !sv.isError();
- return sv.toVariant();
+ class DummyLogSink : public ILogSink {
+ void doPrintMessage(LoggerLevel, const QString &, const QString &) override { }
+ } logSink;
+ Internal::Logger logger(&logSink);
+
+ const auto engine = Internal::ScriptEngine::create(logger, {});
+ Internal::ScopedJsValue sv(
+ engine->context(),
+ engine->evaluate(Internal::JsValueOwner::Caller,
+ QLatin1String("(function(){return ") + str
+ + QLatin1String(";})()")));
+ ok = !engine->checkAndClearException({});
+ return Internal::getJsVariant(engine->context(), sv);
}
QVariant representationToSettingsValue(const QString &representation)
diff --git a/src/lib/corelib/tools/stringconstants.h b/src/lib/corelib/tools/stringconstants.h
index f40bfa4a7..45903032c 100644
--- a/src/lib/corelib/tools/stringconstants.h
+++ b/src/lib/corelib/tools/stringconstants.h
@@ -168,6 +168,7 @@ public:
QBS_STRING_CONSTANT(importScopeNamePropertyInternal, "_qbs_importScopeName")
QBS_STRING_CONSTANT(modulePropertyInternal, "__module")
+ QBS_STRING_CONSTANT(dataPropertyInternal, "_qbs_data")
QBS_STRING_CONSTANT(qbsSourceDirPropertyInternal, "_qbs_sourceDir")
static const char *qbsProcEnvVarInternal() { return "_qbs_procenv"; }
diff --git a/src/lib/libs.qbs b/src/lib/libs.qbs
index 10890bb46..8638ab8dc 100644
--- a/src/lib/libs.qbs
+++ b/src/lib/libs.qbs
@@ -3,6 +3,5 @@ Project {
"corelib/corelib.qbs",
"msbuild/msbuild.qbs",
"pkgconfig/pkgconfig.qbs",
- "scriptengine/scriptengine.qbs",
]
}
diff --git a/src/lib/scriptengine/CMakeLists.txt b/src/lib/scriptengine/CMakeLists.txt
deleted file mode 100644
index 7e5a60d69..000000000
--- a/src/lib/scriptengine/CMakeLists.txt
+++ /dev/null
@@ -1,367 +0,0 @@
-set(QT_SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../shared/qtscript/src/")
-
-if (UNIX)
- set(_USE_SYSTEM_MALLOC NO)
-else()
- set(_USE_SYSTEM_MALLOC YES)
-endif (UNIX)
-
-set(JAVASCRIPT_CORE_INCLUDES
- assembler
- bytecode
- bytecompiler
- debugger
- interpreter
- jit
- parser
- pcre
- profiler
- runtime
- wrec
- wtf
- wtf/unicode
- yarr
- API
- ForwardingHeaders
- generated
-)
-list_transform_prepend(JAVASCRIPT_CORE_INCLUDES "${QT_SCRIPT_PATH}/3rdparty/javascriptcore/JavaScriptCore/")
-
-set(QT_SCRIPT_PUBLIC_INCLUDES
- "${CMAKE_CURRENT_SOURCE_DIR}/include"
- "${CMAKE_CURRENT_BINARY_DIR}/include"
-)
-
-set(QT_SCRIPT_INCLUDES
- "${QT_SCRIPT_PATH}/3rdparty/javascriptcore"
- "${QT_SCRIPT_PATH}/3rdparty/javascriptcore/JavaScriptCore"
- "${QT_SCRIPT_PATH}/script"
- "${QT_SCRIPT_PATH}/script/api"
- "${QT_SCRIPT_PATH}/script/parser"
- "${CMAKE_CURRENT_BINARY_DIR}/include/QtScript/${QT_VERSION}/QtScript"
-)
-
-set(QT_SCRIPT_DEFINES
- "QT_BUILD_SCRIPT_LIB"
- "QT_NO_USING_NAMESPACE"
- "JSC=QTJSC"
- "jscyyparse=qtjscyyparse"
- "jscyylex=qtjscyylex"
- "jscyyerror=qtjscyyerror"
- "WTF=QTWTF"
- "LOG_DISABLED=1"
- "WTF_USE_JAVASCRIPTCORE_BINDINGS=1"
- "WTF_CHANGES=1"
- "JS_NO_EXPORT"
-
- # JavaScriptCore
- "BUILDING_QT__"
- "BUILDING_JavaScriptCore"
- "BUILDING_WTF"
- "ENABLE_JIT=0"
- "ENABLE_YARR_JIT=0"
- "ENABLE_YARR=0"
-
- # WebKit
- "WTF_USE_ACCELERATED_COMPOSITING"
-)
-
-if (APPLE)
- list(APPEND QT_SCRIPT_DEFINES "ENABLE_JSC_MULTIPLE_THREADS=0")
-elseif(WIN32)
- list(APPEND QT_SCRIPT_DEFINES "NOMINMAX" "_CRT_RAND_S")
-endif()
-
-if (MSVC)
- set(QT_SCRIPT_CXX_FLAGS "/wd4018" "/wd4146" "/wd4244" "/wd4267" "/wd4291"
- "/wd4334" "/wd4344" "/wd4396" "/wd4503" "/wd4800" "/wd4819" "/wd4996")
-else()
- set(QT_SCRIPT_CXX_FLAGS "-fno-strict-aliasing" "-w"
- "-Wall" "-Wreturn-type" "-Wcast-align" "-Wchar-subscripts"
- "-Wformat-security" "-Wreturn-type" "-Wno-unused-parameter"
- "-Wno-sign-compare" "-Wno-switch" "-Wno-switch-enum" "-Wundef"
- "-Wmissing-noreturn" "-Winit-self" "-Wno-deprecated" "-Wno-suggest-attribute=noreturn"
- "-Wno-nonnull-compare" "-pthread")
-endif()
-
-set(QT_SCRIPT_PUBLIC_DEPENDS Qt${QT_VERSION_MAJOR}::Core)
-if (UNIX)
- list(APPEND QT_SCRIPT_PUBLIC_DEPENDS "pthread")
-elseif(WIN32)
- list(APPEND QT_SCRIPT_PUBLIC_DEPENDS "winmm")
-endif()
-
-set(PCRE_SOURCES
- pcre_compile.cpp
- pcre_exec.cpp
- pcre_tables.cpp
- pcre_ucp_searchfuncs.cpp
- pcre_xclass.cpp
-)
-list_transform_prepend(PCRE_SOURCES "${QT_SCRIPT_PATH}/3rdparty/javascriptcore/JavaScriptCore/pcre/")
-
-if (NOT _USE_SYSTEM_MALLOC)
- set(SYSTEM_MALLOC_SOURCES
- wtf/TCSystemAlloc.cpp
- )
- list_transform_prepend(SYSTEM_MALLOC_SOURCES "${QT_SCRIPT_PATH}/3rdparty/javascriptcore/JavaScriptCore/")
-
-else()
- set(SYSTEM_MALLOC_SOURCES "")
- list(APPEND QT_SCRIPT_DEFINES "USE_SYSTEM_MALLOC")
-endif()
-
-set(JAVASCRIPT_CORE_SOURCES
- API/JSBase.cpp
- API/JSCallbackConstructor.cpp
- API/JSCallbackFunction.cpp
- API/JSCallbackObject.cpp
- API/JSClassRef.cpp
- API/JSContextRef.cpp
- API/JSObjectRef.cpp
- API/JSStringRef.cpp
- API/JSValueRef.cpp
- API/OpaqueJSString.cpp
- assembler/ARMAssembler.cpp
- assembler/MacroAssemblerARM.cpp
- bytecode/CodeBlock.cpp
- bytecode/JumpTable.cpp
- bytecode/Opcode.cpp
- bytecode/SamplingTool.cpp
- bytecode/StructureStubInfo.cpp
- bytecompiler/BytecodeGenerator.cpp
- bytecompiler/NodesCodegen.cpp
- debugger/DebuggerActivation.cpp
- debugger/DebuggerCallFrame.cpp
- debugger/Debugger.cpp
- generated/Grammar.cpp
- interpreter/CallFrame.cpp
- interpreter/Interpreter.cpp
- interpreter/RegisterFile.cpp
- parser/Lexer.cpp
- parser/Nodes.cpp
- parser/ParserArena.cpp
- parser/Parser.cpp
- profiler/Profile.cpp
- profiler/ProfileGenerator.cpp
- profiler/ProfileNode.cpp
- profiler/Profiler.cpp
- runtime/ArgList.cpp
- runtime/Arguments.cpp
- runtime/ArrayConstructor.cpp
- runtime/ArrayPrototype.cpp
- runtime/BooleanConstructor.cpp
- runtime/BooleanObject.cpp
- runtime/BooleanPrototype.cpp
- runtime/CallData.cpp
- runtime/Collector.cpp
- runtime/CommonIdentifiers.cpp
- runtime/Completion.cpp
- runtime/ConstructData.cpp
- runtime/DateConstructor.cpp
- runtime/DateConversion.cpp
- runtime/DateInstance.cpp
- runtime/DatePrototype.cpp
- runtime/ErrorConstructor.cpp
- runtime/Error.cpp
- runtime/ErrorInstance.cpp
- runtime/ErrorPrototype.cpp
- runtime/ExceptionHelpers.cpp
- runtime/Executable.cpp
- runtime/FunctionConstructor.cpp
- runtime/FunctionPrototype.cpp
- runtime/GetterSetter.cpp
- runtime/GlobalEvalFunction.cpp
- runtime/Identifier.cpp
- runtime/InitializeThreading.cpp
- runtime/InternalFunction.cpp
- runtime/JSActivation.cpp
- runtime/JSAPIValueWrapper.cpp
- runtime/JSArray.cpp
- runtime/JSByteArray.cpp
- runtime/JSCell.cpp
- runtime/JSFunction.cpp
- runtime/JSGlobalData.cpp
- runtime/JSGlobalObject.cpp
- runtime/JSGlobalObjectFunctions.cpp
- runtime/JSImmediate.cpp
- runtime/JSLock.cpp
- runtime/JSNotAnObject.cpp
- runtime/JSNumberCell.cpp
- runtime/JSObject.cpp
- runtime/JSONObject.cpp
- runtime/JSPropertyNameIterator.cpp
- runtime/JSStaticScopeObject.cpp
- runtime/JSString.cpp
- runtime/JSValue.cpp
- runtime/JSVariableObject.cpp
- runtime/JSWrapperObject.cpp
- runtime/LiteralParser.cpp
- runtime/Lookup.cpp
- runtime/MarkStackPosix.cpp
- runtime/MarkStackSymbian.cpp
- runtime/MarkStackWin.cpp
- runtime/MarkStack.cpp
- runtime/MathObject.cpp
- runtime/NativeErrorConstructor.cpp
- runtime/NativeErrorPrototype.cpp
- runtime/NumberConstructor.cpp
- runtime/NumberObject.cpp
- runtime/NumberPrototype.cpp
- runtime/ObjectConstructor.cpp
- runtime/ObjectPrototype.cpp
- runtime/Operations.cpp
- runtime/PropertyDescriptor.cpp
- runtime/PropertyNameArray.cpp
- runtime/PropertySlot.cpp
- runtime/PrototypeFunction.cpp
- runtime/RegExpConstructor.cpp
- runtime/RegExp.cpp
- runtime/RegExpObject.cpp
- runtime/RegExpPrototype.cpp
- runtime/ScopeChain.cpp
- runtime/SmallStrings.cpp
- runtime/StringConstructor.cpp
- runtime/StringObject.cpp
- runtime/StringPrototype.cpp
- runtime/StructureChain.cpp
- runtime/Structure.cpp
- runtime/TimeoutChecker.cpp
- runtime/UString.cpp
- runtime/UStringImpl.cpp
- wtf/Assertions.cpp
- wtf/ByteArray.cpp
- wtf/CurrentTime.cpp
- wtf/DateMath.cpp
- wtf/dtoa.cpp
- wtf/FastMalloc.cpp
- wtf/HashTable.cpp
- wtf/MainThread.cpp
- wtf/qt/MainThreadQt.cpp
- wtf/qt/ThreadingQt.cpp
- wtf/RandomNumber.cpp
- wtf/RefCountedLeakCounter.cpp
- wtf/ThreadingNone.cpp
- wtf/Threading.cpp
- wtf/TypeTraits.cpp
- wtf/unicode/CollatorDefault.cpp
- wtf/unicode/icu/CollatorICU.cpp
- wtf/unicode/UTF8.cpp
-)
-list_transform_prepend(JAVASCRIPT_CORE_SOURCES "${QT_SCRIPT_PATH}/3rdparty/javascriptcore/JavaScriptCore/")
-
-set(API_SOURCES
- qscriptable.cpp
- qscriptable.h
- qscriptable_p.h
- qscriptclass.cpp
- qscriptclass.h
- qscriptclasspropertyiterator.cpp
- qscriptclasspropertyiterator.h
- qscriptcontext.cpp
- qscriptcontext.h
- qscriptcontextinfo.cpp
- qscriptcontextinfo.h
- qscriptcontext_p.h
- qscriptengineagent.cpp
- qscriptengineagent.h
- qscriptengineagent_p.h
- qscriptengine.cpp
- qscriptengine.h
- qscriptengine_p.h
- qscriptextensioninterface.h
- qscriptextensionplugin.cpp
- qscriptextensionplugin.h
- qscriptprogram.cpp
- qscriptprogram.h
- qscriptprogram_p.h
- qscriptstring.cpp
- qscriptstring.h
- qscriptstring_p.h
- qscriptvalue.cpp
- qscriptvalue.h
- qscriptvalueiterator.cpp
- qscriptvalueiterator.h
- qscriptvalue_p.h
- qtscriptglobal.h
-)
-list_transform_prepend(API_SOURCES "${QT_SCRIPT_PATH}/script/api/")
-
-set(BRIDGE_SOURCES
- qscriptactivationobject.cpp
- qscriptactivationobject_p.h
- qscriptclassobject.cpp
- qscriptclassobject_p.h
- qscriptfunction.cpp
- qscriptfunction_p.h
- qscriptglobalobject.cpp
- qscriptglobalobject_p.h
- qscriptobject.cpp
- qscriptobject_p.h
- qscriptqobject.cpp
- qscriptqobject_p.h
- qscriptstaticscopeobject.cpp
- qscriptstaticscopeobject_p.h
- qscriptvariant.cpp
- qscriptvariant_p.h
-)
-list_transform_prepend(BRIDGE_SOURCES "${QT_SCRIPT_PATH}/script/bridge/")
-
-set(PARSER_SOURCES
- qscriptast.cpp
- qscriptastfwd_p.h
- qscriptast_p.h
- qscriptastvisitor.cpp
- qscriptastvisitor_p.h
- qscriptgrammar.cpp
- qscriptgrammar_p.h
- qscriptlexer.cpp
- qscriptlexer_p.h
- qscriptsyntaxchecker.cpp
- qscriptsyntaxchecker_p.h
-)
-list_transform_prepend(PARSER_SOURCES "${QT_SCRIPT_PATH}/script/parser/")
-
-find_package(Perl)
-
-get_target_property(_QT_MOC_EXECUTABLE Qt${QT_VERSION_MAJOR}::moc IMPORTED_LOCATION)
-get_filename_component(_QT_LIBEXEC_DIRECTORY ${_QT_MOC_EXECUTABLE} DIRECTORY)
-
-file(GLOB API_HEADERS "${QT_SCRIPT_PATH}/script/api/*.h")
-
-set(EXTERNAL_DEPENDS "")
-if(APPLE)
- set(EXTERNAL_DEPENDS "-framework CoreFoundation")
-endif()
-
-if(WIN32)
- set(EXTERNAL_DEPENDS "winmm")
-endif()
-
-add_custom_command(
- OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/QtScript/qscriptengine.h"
- COMMAND ${PERL_EXECUTABLE} "${_QT_LIBEXEC_DIRECTORY}/syncqt.pl"
- -minimal
- -version "${QT_VERSION}"
- -outdir ${CMAKE_CURRENT_BINARY_DIR}
- "${QT_SCRIPT_PATH}/.."
- BYPRODUCTS
- DEPENDS ${API_HEADERS}
- )
-
-add_qbs_library(qbsscriptengine
- DEFINES ${QT_SCRIPT_DEFINES}
- DEPENDS Qt${QT_VERSION_MAJOR}::CorePrivate Qt6Core5Compat ${EXTERNAL_DEPENDS}
- PUBLIC_DEPENDS ${QT_SCRIPT_PUBLIC_DEPENDS}
- INCLUDES ${QT_SCRIPT_PUBLIC_INCLUDES} ${QT_SCRIPT_INCLUDES} ${JAVASCRIPT_CORE_INCLUDES}
- PUBLIC_INCLUDES ${QT_SCRIPT_PUBLIC_INCLUDES}
- SOURCES
- "${CMAKE_CURRENT_BINARY_DIR}/include/QtScript/qscriptengine.h"
- ${PCRE_SOURCES}
- ${SYSTEM_MALLOC_SOURCES}
- ${JAVASCRIPT_CORE_SOURCES}
- ${API_SOURCES}
- ${BRIDGE_SOURCES}
- ${PARSER_SOURCES}
- )
-target_compile_options(qbsscriptengine PRIVATE ${QT_SCRIPT_CXX_FLAGS})
diff --git a/src/lib/scriptengine/scriptengine.qbs b/src/lib/scriptengine/scriptengine.qbs
deleted file mode 100644
index fcbd6ffaf..000000000
--- a/src/lib/scriptengine/scriptengine.qbs
+++ /dev/null
@@ -1,464 +0,0 @@
-import qbs.File
-import qbs.FileInfo
-import qbs.Probes
-import qbs.Process
-import qbs.Utilities
-
-Project {
- QbsLibrary {
- condition: qbsbuildconfig.useBundledQtScript || !Qt.script.present
- Depends {
- name: "Qt.script"
- condition: !qbsbuildconfig.useBundledQtScript
- required: false
- }
- Depends { name: "QtScriptFwdHeaders" }
- Depends { name: "cpp" }
- Depends { name: "Qt"; submodules: ["core-private"] }
- Depends {
- name: "Qt.core5compat";
- condition: Utilities.versionCompare(Qt.core.version, "6.0.0") >= 0
- }
- name: "qbsscriptengine"
-
- generatePkgConfigFile: false
- generateQbsModule: false
-
- property bool useSystemMalloc: !qbs.targetOS.contains("macos")
- && !qbs.targetOS.contains("unix")
- property string qtscriptPath: "../../shared/qtscript/src/"
- cpp.includePaths: {
- var result = base.concat(
- ".",
- "include"
- );
-
- var jscBaseDir = qtscriptPath + "3rdparty/javascriptcore";
- result.push(jscBaseDir);
- jscBaseDir += "/JavaScriptCore";
- result.push(jscBaseDir);
-
- var jscSubDirs = [
- "assembler",
- "bytecode",
- "bytecompiler",
- "debugger",
- "interpreter",
- "jit",
- "parser",
- "pcre",
- "profiler",
- "runtime",
- "wrec",
- "wtf",
- "wtf/unicode",
- "yarr",
- "API",
- "ForwardingHeaders",
- "generated"];
- result = result.concat(jscSubDirs.map(function(s) { return jscBaseDir + '/' + s; }));
-
- result.push(qtscriptPath + "script");
- result.push(qtscriptPath + "script/api");
- result.push(qtscriptPath + "script/parser");
- result = result.concat(QtScriptFwdHeaders.publicIncludePaths,
- QtScriptFwdHeaders.privateIncludePaths);
- return result;
- }
- cpp.defines: {
- var result = base.concat([
- "QT_BUILD_SCRIPT_LIB", "QT_NO_USING_NAMESPACE",
- "JSC=QTJSC", "jscyyparse=qtjscyyparse", "jscyylex=qtjscyylex",
- "jscyyerror=qtjscyyerror",
- "WTF=QTWTF",
- "LOG_DISABLED=1",
- "WTF_USE_JAVASCRIPTCORE_BINDINGS=1", "WTF_CHANGES=1",
- "JS_NO_EXPORT"]);
- if (qbs.buildVariant != "debug")
- result.push("NDEBUG");
- if (qbs.targetOS.contains("macos"))
- result.push("ENABLE_JSC_MULTIPLE_THREADS=0");
-
- // JavaScriptCore
- result.push("BUILDING_QT__", "BUILDING_JavaScriptCore", "BUILDING_WTF",
- "ENABLE_JIT=0", "ENABLE_YARR_JIT=0", "ENABLE_YARR=0");
- if (qbs.targetOS.contains("windows")) {
- // Prevent definition of min, max macros in windows.h
- result.push("NOMINMAX");
- // Enables rand_s
- result.push("_CRT_RAND_S");
- }
-
- // WebKit
- result.push("WTF_USE_ACCELERATED_COMPOSITING");
- if (useSystemMalloc)
- result.push("USE_SYSTEM_MALLOC");
-
- result = result.filter(function(value) {
- return value !== "QT_RESTRICTED_CAST_FROM_ASCII"
- && value !== "QT_NO_CAST_FROM_BYTEARRAY"
- && value !== "QT_NO_CAST_FROM_ASCII";
- })
- return result;
- }
- cpp.cxxFlags: {
- var result = base;
- if (qbs.toolchain.contains("gcc")) {
- result.push("-fno-strict-aliasing",
- "-Wall", "-Wreturn-type", "-Wcast-align", "-Wchar-subscripts",
- "-Wformat-security", "-Wreturn-type", "-Wno-unused-parameter",
- "-Wno-sign-compare", "-Wno-switch", "-Wno-switch-enum", "-Wundef",
- "-Wmissing-noreturn", "-Winit-self", "-Wno-deprecated",
- "-Wno-suggest-attribute=noreturn", "-Wno-nonnull-compare");
- } else if (qbs.toolchain.contains("msvc")) {
- result.push("-wd4291", "-wd4344", "-wd4396", "-wd4503", "-wd4800", "-wd4819",
- "-wd4996");
- }
- if (qbs.targetOS.contains("unix"))
- result.push("-pthread");
-
- return result;
- }
- cpp.warningLevel: "none"
- cpp.optimization: "fast" // We cannot afford -O0 for QtScript even in debug builds.
- cpp.frameworks: base.concat(qbs.targetOS.contains("darwin") ? ["CoreFoundation"] : [])
- Properties {
- condition: qbs.targetOS.contains("unix")
- cpp.dynamicLibraries: base.concat(["pthread"])
- }
- Properties {
- condition: qbs.targetOS.contains("windows")
- cpp.dynamicLibraries: base.concat(["winmm"])
- }
-
- Group {
- name: "pcre"
- prefix: qtscriptPath + "3rdparty/javascriptcore/JavaScriptCore/pcre/"
- files: [
- "pcre_compile.cpp",
- "pcre_exec.cpp",
- "pcre_tables.cpp",
- "pcre_ucp_searchfuncs.cpp",
- "pcre_xclass.cpp",
- ]
- }
-
- Group {
- name: "system malloc replacement"
- prefix: qtscriptPath + "3rdparty/javascriptcore/JavaScriptCore/"
- condition: !useSystemMalloc
- files: [
- "wtf/TCSystemAlloc.cpp",
- ]
- }
-
- Group {
- name: "JavaScriptCore"
- prefix: qtscriptPath + "3rdparty/javascriptcore/JavaScriptCore/"
- files: [
- "API/JSBase.cpp",
- "API/JSCallbackConstructor.cpp",
- "API/JSCallbackFunction.cpp",
- "API/JSCallbackObject.cpp",
- "API/JSClassRef.cpp",
- "API/JSContextRef.cpp",
- "API/JSObjectRef.cpp",
- "API/JSStringRef.cpp",
- "API/JSValueRef.cpp",
- "API/OpaqueJSString.cpp",
- "assembler/ARMAssembler.cpp",
- "assembler/MacroAssemblerARM.cpp",
- "bytecode/CodeBlock.cpp",
- "bytecode/JumpTable.cpp",
- "bytecode/Opcode.cpp",
- "bytecode/SamplingTool.cpp",
- "bytecode/StructureStubInfo.cpp",
- "bytecompiler/BytecodeGenerator.cpp",
- "bytecompiler/NodesCodegen.cpp",
- "debugger/DebuggerActivation.cpp",
- "debugger/DebuggerCallFrame.cpp",
- "debugger/Debugger.cpp",
- "generated/Grammar.cpp",
- "interpreter/CallFrame.cpp",
- "interpreter/Interpreter.cpp",
- "interpreter/RegisterFile.cpp",
- "parser/Lexer.cpp",
- "parser/Nodes.cpp",
- "parser/ParserArena.cpp",
- "parser/Parser.cpp",
- "profiler/Profile.cpp",
- "profiler/ProfileGenerator.cpp",
- "profiler/ProfileNode.cpp",
- "profiler/Profiler.cpp",
- "runtime/ArgList.cpp",
- "runtime/Arguments.cpp",
- "runtime/ArrayConstructor.cpp",
- "runtime/ArrayPrototype.cpp",
- "runtime/BooleanConstructor.cpp",
- "runtime/BooleanObject.cpp",
- "runtime/BooleanPrototype.cpp",
- "runtime/CallData.cpp",
- "runtime/Collector.cpp",
- "runtime/CommonIdentifiers.cpp",
- "runtime/Completion.cpp",
- "runtime/ConstructData.cpp",
- "runtime/DateConstructor.cpp",
- "runtime/DateConversion.cpp",
- "runtime/DateInstance.cpp",
- "runtime/DatePrototype.cpp",
- "runtime/ErrorConstructor.cpp",
- "runtime/Error.cpp",
- "runtime/ErrorInstance.cpp",
- "runtime/ErrorPrototype.cpp",
- "runtime/ExceptionHelpers.cpp",
- "runtime/Executable.cpp",
- "runtime/FunctionConstructor.cpp",
- "runtime/FunctionPrototype.cpp",
- "runtime/GetterSetter.cpp",
- "runtime/GlobalEvalFunction.cpp",
- "runtime/Identifier.cpp",
- "runtime/InitializeThreading.cpp",
- "runtime/InternalFunction.cpp",
- "runtime/JSActivation.cpp",
- "runtime/JSAPIValueWrapper.cpp",
- "runtime/JSArray.cpp",
- "runtime/JSByteArray.cpp",
- "runtime/JSCell.cpp",
- "runtime/JSFunction.cpp",
- "runtime/JSGlobalData.cpp",
- "runtime/JSGlobalObject.cpp",
- "runtime/JSGlobalObjectFunctions.cpp",
- "runtime/JSImmediate.cpp",
- "runtime/JSLock.cpp",
- "runtime/JSNotAnObject.cpp",
- "runtime/JSNumberCell.cpp",
- "runtime/JSObject.cpp",
- "runtime/JSONObject.cpp",
- "runtime/JSPropertyNameIterator.cpp",
- "runtime/JSStaticScopeObject.cpp",
- "runtime/JSString.cpp",
- "runtime/JSValue.cpp",
- "runtime/JSVariableObject.cpp",
- "runtime/JSWrapperObject.cpp",
- "runtime/LiteralParser.cpp",
- "runtime/Lookup.cpp",
- "runtime/MarkStackPosix.cpp",
- "runtime/MarkStackSymbian.cpp",
- "runtime/MarkStackWin.cpp",
- "runtime/MarkStack.cpp",
- "runtime/MathObject.cpp",
- "runtime/NativeErrorConstructor.cpp",
- "runtime/NativeErrorPrototype.cpp",
- "runtime/NumberConstructor.cpp",
- "runtime/NumberObject.cpp",
- "runtime/NumberPrototype.cpp",
- "runtime/ObjectConstructor.cpp",
- "runtime/ObjectPrototype.cpp",
- "runtime/Operations.cpp",
- "runtime/PropertyDescriptor.cpp",
- "runtime/PropertyNameArray.cpp",
- "runtime/PropertySlot.cpp",
- "runtime/PrototypeFunction.cpp",
- "runtime/RegExpConstructor.cpp",
- "runtime/RegExp.cpp",
- "runtime/RegExpObject.cpp",
- "runtime/RegExpPrototype.cpp",
- "runtime/ScopeChain.cpp",
- "runtime/SmallStrings.cpp",
- "runtime/StringConstructor.cpp",
- "runtime/StringObject.cpp",
- "runtime/StringPrototype.cpp",
- "runtime/StructureChain.cpp",
- "runtime/Structure.cpp",
- "runtime/TimeoutChecker.cpp",
- "runtime/UString.cpp",
- "runtime/UStringImpl.cpp",
- "wtf/Assertions.cpp",
- "wtf/ByteArray.cpp",
- "wtf/CurrentTime.cpp",
- "wtf/DateMath.cpp",
- "wtf/dtoa.cpp",
- "wtf/FastMalloc.cpp",
- "wtf/HashTable.cpp",
- "wtf/MainThread.cpp",
- "wtf/qt/MainThreadQt.cpp",
- "wtf/qt/ThreadingQt.cpp",
- "wtf/RandomNumber.cpp",
- "wtf/RefCountedLeakCounter.cpp",
- "wtf/ThreadingNone.cpp",
- "wtf/Threading.cpp",
- "wtf/TypeTraits.cpp",
- "wtf/unicode/CollatorDefault.cpp",
- "wtf/unicode/icu/CollatorICU.cpp",
- "wtf/unicode/UTF8.cpp",
- ]
- }
- Group {
- name: "api"
- prefix: qtscriptPath + "script/api/"
- files: [
- "qscriptable.cpp",
- "qscriptable.h",
- "qscriptable_p.h",
- "qscriptclass.cpp",
- "qscriptclass.h",
- "qscriptclasspropertyiterator.cpp",
- "qscriptclasspropertyiterator.h",
- "qscriptcontext.cpp",
- "qscriptcontext.h",
- "qscriptcontextinfo.cpp",
- "qscriptcontextinfo.h",
- "qscriptcontext_p.h",
- "qscriptengineagent.cpp",
- "qscriptengineagent.h",
- "qscriptengineagent_p.h",
- "qscriptengine.cpp",
- "qscriptengine.h",
- "qscriptengine_p.h",
- "qscriptextensioninterface.h",
- "qscriptextensionplugin.cpp",
- "qscriptextensionplugin.h",
- "qscriptprogram.cpp",
- "qscriptprogram.h",
- "qscriptprogram_p.h",
- "qscriptstring.cpp",
- "qscriptstring.h",
- "qscriptstring_p.h",
- "qscriptvalue.cpp",
- "qscriptvalue.h",
- "qscriptvalueiterator.cpp",
- "qscriptvalueiterator.h",
- "qscriptvalue_p.h",
- "qtscriptglobal.h",
- ]
- }
- Group {
- name: "bridge"
- prefix: qtscriptPath + "script/bridge/"
- files: [
- "qscriptactivationobject.cpp",
- "qscriptactivationobject_p.h",
- "qscriptclassobject.cpp",
- "qscriptclassobject_p.h",
- "qscriptfunction.cpp",
- "qscriptfunction_p.h",
- "qscriptglobalobject.cpp",
- "qscriptglobalobject_p.h",
- "qscriptobject.cpp",
- "qscriptobject_p.h",
- "qscriptqobject.cpp",
- "qscriptqobject_p.h",
- "qscriptstaticscopeobject.cpp",
- "qscriptstaticscopeobject_p.h",
- "qscriptvariant.cpp",
- "qscriptvariant_p.h",
- ]
- }
- Group {
- name: "parser"
- prefix: qtscriptPath + "script/parser/"
- files: [
- "qscriptast.cpp",
- "qscriptastfwd_p.h",
- "qscriptast_p.h",
- "qscriptastvisitor.cpp",
- "qscriptastvisitor_p.h",
- "qscriptgrammar.cpp",
- "qscriptgrammar_p.h",
- "qscriptlexer.cpp",
- "qscriptlexer_p.h",
- "qscriptsyntaxchecker.cpp",
- "qscriptsyntaxchecker_p.h",
- ]
- }
-
- Export {
- Depends { name: "QtScriptFwdHeaders" }
- Depends { name: "cpp" }
- property stringList includePaths: [exportingProduct.sourceDirectory + "/include"]
- .concat(QtScriptFwdHeaders.publicIncludePaths)
- Properties {
- condition: qbs.targetOS.contains("unix")
- cpp.dynamicLibraries: base.concat(["pthread"])
- }
- Properties {
- condition: qbs.targetOS.contains("windows")
- cpp.dynamicLibraries: base.concat(["winmm"])
- }
- }
- }
- Product {
- type: ["hpp"]
- name: "QtScriptFwdHeaders"
- condition: qbsbuildconfig.useBundledQtScript || !Qt.script.present
- Depends { name: "qbsbuildconfig" }
- Depends {
- name: "Qt.script"
- condition: !qbsbuildconfig.useBundledQtScript
- required: false
- }
- Depends { name: "Qt.core" }
- Group {
- files: [
- "../../shared/qtscript/src/script/api/*.h"
- ]
- fileTags: ["qtscriptheader"]
- }
- Probes.BinaryProbe {
- id: perlProbe
- names: "perl"
- }
- property string perlPath: perlProbe.found ? perlProbe.filePath : undefined
- Rule {
- multiplex: true
- inputs: ["qtscriptheader"]
- Artifact {
- filePath: "include/QtScript/qscriptengine.h"
- fileTags: ["hpp"]
- }
- prepare: {
- var syncQtPath;
- if (Utilities.versionCompare(product.Qt.core.version, "6.1") >= 0) {
- syncQtPath = FileInfo.joinPaths(product.Qt.core.libExecPath, "syncqt.pl");
- } else {
- syncQtPath = FileInfo.joinPaths(product.Qt.core.binPath, "syncqt.pl");
- }
- if (!File.exists(syncQtPath)) {
- // syncqt.pl is not in Qt's bin path. We might have a developer build.
- // As we don't provide QT_HOST_BINS/src in our Qt modules we must
- // kindly ask qmake.
- var qmake = FileInfo.joinPaths(product.Qt.core.binPath,
- "qmake" + product.cpp.executableSuffix);
- var p = new Process();
- if (p.exec(qmake, ["-query", "QT_HOST_BINS/src"]) !== 0)
- throw new Error("Error while querying qmake.");
- syncQtPath = FileInfo.joinPaths(p.readStdOut().replace(/\r?\n/, ''),
- "syncqt.pl");
- }
- var qtScriptSrcPath = FileInfo.cleanPath(
- FileInfo.path(inputs["qtscriptheader"][0].filePath) + "/../../..");
- console.info("qtScriptSrcPath: " + qtScriptSrcPath);
- var cmd = new Command(product.perlPath, [
- syncQtPath,
- "-minimal",
- "-version", product.Qt.core.version,
- "-outdir", FileInfo.cleanPath(
- FileInfo.path(output.filePath) + "/../.."),
- qtScriptSrcPath
- ]);
- cmd.description = "creating forwarding headers for the bundled QtScript module.";
- return cmd;
- }
- }
- Export {
- Depends { name: "Qt.core" }
- property stringList publicIncludePaths: [
- FileInfo.joinPaths(exportingProduct.buildDirectory, "include")
- ]
- property stringList privateIncludePaths: [
- FileInfo.joinPaths(exportingProduct.buildDirectory, "include",
- "QtScript", Qt.core.version, "QtScript")
- ]
- }
- }
-}
diff --git a/src/packages/archive/archive.qbs b/src/packages/archive/archive.qbs
index 40ebb5fbe..d1875a1ac 100644
--- a/src/packages/archive/archive.qbs
+++ b/src/packages/archive/archive.qbs
@@ -6,7 +6,6 @@ import qbs.TextFile
QbsProduct {
Depends { name: "qbs_processlauncher" }
Depends { name: "qbscore" }
- Depends { name: "qbsscriptengine"; required: false }
Depends { name: "bundledqt"; required: false }
Depends { name: "qbs documentation"; condition: project.withDocumentation }
Depends { name: "qbs resources" }
diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt
index b77e01b4e..e517f6489 100644
--- a/src/shared/CMakeLists.txt
+++ b/src/shared/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory(json)
+add_subdirectory(quickjs)
add_subdirectory(variant)
diff --git a/src/shared/bundledqt/bundledqt.qbs b/src/shared/bundledqt/bundledqt.qbs
index e0f79df59..39409b938 100644
--- a/src/shared/bundledqt/bundledqt.qbs
+++ b/src/shared/bundledqt/bundledqt.qbs
@@ -6,7 +6,6 @@ Product {
Depends { name: "qbsbuildconfig" }
Depends { name: "Qt"; submodules: ["core", "gui", "network", "printsupport", "widgets", "xml"] }
Depends { name: "Qt.test"; condition: project.withTests === true }
- Depends { name: "Qt.script"; condition: !qbsbuildconfig.useBundledQtScript; required: false }
Depends {
name: "Qt.core5compat";
condition: Utilities.versionCompare(Qt.core.version, "6") >= 0
diff --git a/src/shared/qtscript b/src/shared/qtscript
deleted file mode 160000
-Subproject 2c1ffc66bf5d5db05018d7b06253b5ca51e557a
diff --git a/src/shared/quickjs/.clang-tidy b/src/shared/quickjs/.clang-tidy
new file mode 100644
index 000000000..c5e5db244
--- /dev/null
+++ b/src/shared/quickjs/.clang-tidy
@@ -0,0 +1,13 @@
+---
+Checks: >
+ -bugprone-branch-clone,
+ -bugprone-easily-swappable-parameters,
+ -bugprone-implicit-widening-of-multiplication-result,
+ -bugprone-signed-char-misuse,
+ -bugprone-suspicious-memory-comparison,
+ -bugprone-sizeof-expression,
+ -misc-redundant-expression,
+ -misc-unused-parameters
+
+InheritParentConfig: true
+...
diff --git a/src/shared/quickjs/CMakeLists.txt b/src/shared/quickjs/CMakeLists.txt
new file mode 100644
index 000000000..a0af03c79
--- /dev/null
+++ b/src/shared/quickjs/CMakeLists.txt
@@ -0,0 +1,33 @@
+add_qbs_library(qbsquickjs
+ STATIC
+ SOURCES
+ cutils.c cutils.h
+ libregexp-opcode.h
+ libregexp.c libregexp.h
+ libunicode-table.h
+ libunicode.c libunicode.h
+ list.h
+ quickjs-atom.h
+ quickjs-opcode.h
+ quickjs.c quickjs.h
+ DEFINES
+ "CONFIG_VERSION=\"2021-03-27\""
+)
+if(QBS_QUICKJS_LEAK_CHECK)
+ add_definitions(-DDUMP_LEAKS)
+endif()
+target_include_directories(
+ qbsquickjs
+ SYSTEM
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+add_library(qbsquickjsheaders INTERFACE)
+target_include_directories(
+ qbsquickjsheaders
+ SYSTEM
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+)
+if(MSVC)
+ target_compile_options(qbsquickjs PUBLIC /wd4018 /wd4334 /wd4101 /wd4146 /wd4244 /wd4267 /wd4996)
+endif()
diff --git a/src/shared/quickjs/LICENSE b/src/shared/quickjs/LICENSE
new file mode 100644
index 000000000..2c8fdebaf
--- /dev/null
+++ b/src/shared/quickjs/LICENSE
@@ -0,0 +1,22 @@
+QuickJS Javascript Engine
+
+Copyright (c) 2017-2021 Fabrice Bellard
+Copyright (c) 2017-2021 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.
diff --git a/src/shared/quickjs/cutils.c b/src/shared/quickjs/cutils.c
new file mode 100644
index 000000000..1f66fff3f
--- /dev/null
+++ b/src/shared/quickjs/cutils.c
@@ -0,0 +1,630 @@
+/*
+ * 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 FORMAT_ATTR(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: case 0xc1: case 0xc2: case 0xc3:
+ case 0xc4: case 0xc5: case 0xc6: case 0xc7:
+ case 0xc8: case 0xc9: case 0xca: case 0xcb:
+ case 0xcc: case 0xcd: case 0xce: case 0xcf:
+ case 0xd0: case 0xd1: case 0xd2: case 0xd3:
+ case 0xd4: case 0xd5: case 0xd6: case 0xd7:
+ case 0xd8: case 0xd9: case 0xda: case 0xdb:
+ case 0xdc: case 0xdd: case 0xde: case 0xdf:
+ l = 1;
+ break;
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3:
+ case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb:
+ case 0xec: case 0xed: case 0xee: case 0xef:
+ l = 2;
+ break;
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3:
+ case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ l = 3;
+ break;
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb:
+ l = 4;
+ break;
+ case 0xfc: case 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;
+ 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
diff --git a/src/shared/quickjs/cutils.h b/src/shared/quickjs/cutils.h
new file mode 100644
index 000000000..ee0ce4a2e
--- /dev/null
+++ b/src/shared/quickjs/cutils.h
@@ -0,0 +1,309 @@
+/*
+ * 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>
+
+#if defined(_MSC_VER)
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#else
+#include <sys/types.h>
+#endif
+
+/* set if CPU is big endian */
+#undef WORDS_BIGENDIAN
+
+#ifdef __GNUC__
+#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 maybe_unused __attribute__((unused))
+#else
+#define likely(x) (x)
+#define unlikely(x) (x)
+#define force_inline
+#define no_inline
+#define maybe_unused
+#endif
+
+#ifdef _MSC_VER
+#define alloca _alloca
+#endif
+
+#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)
+{
+#ifdef _MSC_VER
+ return (int) __lzcnt(a);
+#else
+ return __builtin_clz(a);
+#endif
+}
+
+#pragma pack(push, 1)
+struct packed_u64 {
+ uint64_t v;
+};
+struct packed_u32 {
+ uint32_t v;
+};
+struct packed_u16 {
+ uint16_t v;
+};
+#pragma pack(pop)
+
+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);
+}
+
+#ifdef __GNUC__
+#define FORMAT_ATTR(x, y) __attribute__((format(printf, x, y)))
+#else
+#define FORMAT_ATTR(x, y)
+#endif
+
+int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...);
+
+void dbuf_free(DynBuf *s);
+static inline BOOL dbuf_error(DynBuf *s) {
+ return s->error;
+}
+static inline void dbuf_set_error(DynBuf *s)
+{
+ s->error = TRUE;
+}
+
+#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 */
diff --git a/src/shared/quickjs/libregexp-opcode.h b/src/shared/quickjs/libregexp-opcode.h
new file mode 100644
index 000000000..f90c23b34
--- /dev/null
+++ b/src/shared/quickjs/libregexp-opcode.h
@@ -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 */
diff --git a/src/shared/quickjs/libregexp.c b/src/shared/quickjs/libregexp.c
new file mode 100644
index 000000000..ad91f781a
--- /dev/null
+++ b/src/shared/quickjs/libregexp.c
@@ -0,0 +1,2610 @@
+/*
+ * 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.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "libregexp.h"
+
+/*
+ TODO:
+
+ - Add full unicode canonicalize rules for character ranges (not
+ really useful but needed for exact "ignorecase" compatibility).
+
+ - Add a lock step execution mode (=linear time execution guaranteed)
+ when the regular expression is "simple" i.e. no backreference nor
+ complicated lookahead. The opcodes are designed for this execution
+ model.
+*/
+
+#if defined(TEST)
+#define DUMP_REOP
+#endif
+
+typedef enum {
+#define DEF(id, size) REOP_ ## id,
+#include "libregexp-opcode.h"
+#undef DEF
+ REOP_COUNT,
+} REOPCodeEnum;
+
+#define CAPTURE_COUNT_MAX 255
+#define STACK_SIZE_MAX 255
+
+/* unicode code points */
+#define CP_LS 0x2028
+#define CP_PS 0x2029
+
+#define TMP_BUF_SIZE 128
+
+typedef struct {
+ DynBuf byte_code;
+ const uint8_t *buf_ptr;
+ const uint8_t *buf_end;
+ const uint8_t *buf_start;
+ int re_flags;
+ BOOL is_utf16;
+ BOOL ignore_case;
+ BOOL dotall;
+ int capture_count;
+ int total_capture_count; /* -1 = not computed yet */
+ int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */
+ void *opaque;
+ DynBuf group_names;
+ union {
+ char error_msg[TMP_BUF_SIZE];
+ char tmp_buf[TMP_BUF_SIZE];
+ } u;
+} REParseState;
+
+typedef struct {
+#ifdef DUMP_REOP
+ const char *name;
+#endif
+ uint8_t size;
+} REOpCode;
+
+static const REOpCode reopcode_info[REOP_COUNT] = {
+#ifdef DUMP_REOP
+#define DEF(id, size) { #id, size },
+#else
+#define DEF(id, size) { size },
+#endif
+#include "libregexp-opcode.h"
+#undef DEF
+};
+
+#define RE_HEADER_FLAGS 0
+#define RE_HEADER_CAPTURE_COUNT 1
+#define RE_HEADER_STACK_SIZE 2
+
+#define RE_HEADER_LEN 7
+
+static inline int is_digit(int c) {
+ return c >= '0' && c <= '9';
+}
+
+/* insert 'len' bytes at position 'pos'. Return < 0 if error. */
+static int dbuf_insert(DynBuf *s, int pos, int len)
+{
+ if (dbuf_realloc(s, s->size + len))
+ return -1;
+ memmove(s->buf + pos + len, s->buf + pos, s->size - pos);
+ s->size += len;
+ return 0;
+}
+
+/* canonicalize with the specific JS regexp rules */
+static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16)
+{
+ uint32_t res[LRE_CC_RES_LEN_MAX];
+ int len;
+ if (is_utf16) {
+ if (likely(c < 128)) {
+ if (c >= 'A' && c <= 'Z')
+ c = c - 'A' + 'a';
+ } else {
+ lre_case_conv(res, c, 2);
+ c = res[0];
+ }
+ } else {
+ if (likely(c < 128)) {
+ if (c >= 'a' && c <= 'z')
+ c = c - 'a' + 'A';
+ } else {
+ /* legacy regexp: to upper case if single char >= 128 */
+ len = lre_case_conv(res, c, FALSE);
+ if (len == 1 && res[0] >= 128)
+ c = res[0];
+ }
+ }
+ return c;
+}
+
+static const uint16_t char_range_d[] = {
+ 1,
+ 0x0030, 0x0039 + 1,
+};
+
+/* code point ranges for Zs,Zl or Zp property */
+static const uint16_t char_range_s[] = {
+ 10,
+ 0x0009, 0x000D + 1,
+ 0x0020, 0x0020 + 1,
+ 0x00A0, 0x00A0 + 1,
+ 0x1680, 0x1680 + 1,
+ 0x2000, 0x200A + 1,
+ /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */
+ /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */
+ 0x2028, 0x2029 + 1,
+ 0x202F, 0x202F + 1,
+ 0x205F, 0x205F + 1,
+ 0x3000, 0x3000 + 1,
+ /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */
+ 0xFEFF, 0xFEFF + 1,
+};
+
+BOOL lre_is_space(int c)
+{
+ int i, n, low, high;
+ n = (countof(char_range_s) - 1) / 2;
+ for(i = 0; i < n; i++) {
+ low = char_range_s[2 * i + 1];
+ if (c < low)
+ return FALSE;
+ high = char_range_s[2 * i + 2];
+ if (c < high)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+uint32_t const lre_id_start_table_ascii[4] = {
+ /* $ A-Z _ a-z */
+ 0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE
+};
+
+uint32_t const lre_id_continue_table_ascii[4] = {
+ /* $ 0-9 A-Z _ a-z */
+ 0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE
+};
+
+
+static const uint16_t char_range_w[] = {
+ 4,
+ 0x0030, 0x0039 + 1,
+ 0x0041, 0x005A + 1,
+ 0x005F, 0x005F + 1,
+ 0x0061, 0x007A + 1,
+};
+
+#define CLASS_RANGE_BASE 0x40000000
+
+typedef enum {
+ CHAR_RANGE_d,
+ CHAR_RANGE_D,
+ CHAR_RANGE_s,
+ CHAR_RANGE_S,
+ CHAR_RANGE_w,
+ CHAR_RANGE_W,
+} CharRangeEnum;
+
+static const uint16_t *char_range_table[] = {
+ char_range_d,
+ char_range_s,
+ char_range_w,
+};
+
+static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c)
+{
+ BOOL invert;
+ const uint16_t *c_pt;
+ int len, i;
+
+ invert = c & 1;
+ c_pt = char_range_table[c >> 1];
+ len = *c_pt++;
+ cr_init(cr, s->opaque, lre_realloc);
+ for(i = 0; i < len * 2; i++) {
+ if (cr_add_point(cr, c_pt[i]))
+ goto fail;
+ }
+ if (invert) {
+ if (cr_invert(cr))
+ goto fail;
+ }
+ return 0;
+ fail:
+ cr_free(cr);
+ return -1;
+}
+
+static int cr_canonicalize(CharRange *cr)
+{
+ CharRange a;
+ uint32_t pt[2];
+ int i, ret;
+
+ cr_init(&a, cr->mem_opaque, lre_realloc);
+ pt[0] = 'a';
+ pt[1] = 'z' + 1;
+ ret = cr_op(&a, cr->points, cr->len, pt, 2, CR_OP_INTER);
+ if (ret)
+ goto fail;
+ /* convert to upper case */
+ /* XXX: the generic unicode case would be much more complicated
+ and not really useful */
+ for(i = 0; i < a.len; i++) {
+ a.points[i] += 'A' - 'a';
+ }
+ /* Note: for simplicity we keep the lower case ranges */
+ ret = cr_union1(cr, a.points, a.len);
+ fail:
+ cr_free(&a);
+ return ret;
+}
+
+#ifdef DUMP_REOP
+static MAYBE_UNUSED void lre_dump_bytecode(const uint8_t *buf,
+ int buf_len)
+{
+ int pos, len, opcode, bc_len, re_flags, i;
+ uint32_t val;
+
+ assert(buf_len >= RE_HEADER_LEN);
+
+ re_flags= buf[0];
+ bc_len = get_u32(buf + 3);
+ assert(bc_len + RE_HEADER_LEN <= buf_len);
+ printf("flags: 0x%x capture_count=%d stack_size=%d\n",
+ re_flags, buf[1], buf[2]);
+ if (re_flags & LRE_FLAG_NAMED_GROUPS) {
+ const char *p;
+ p = (char *)buf + RE_HEADER_LEN + bc_len;
+ printf("named groups: ");
+ for(i = 1; i < buf[1]; i++) {
+ if (i != 1)
+ printf(",");
+ printf("<%s>", p);
+ p += strlen(p) + 1;
+ }
+ printf("\n");
+ assert(p == (char *)(buf + buf_len));
+ }
+ printf("bytecode_len=%d\n", bc_len);
+
+ buf += RE_HEADER_LEN;
+ pos = 0;
+ while (pos < bc_len) {
+ printf("%5u: ", pos);
+ opcode = buf[pos];
+ len = reopcode_info[opcode].size;
+ if (opcode >= REOP_COUNT) {
+ printf(" invalid opcode=0x%02x\n", opcode);
+ break;
+ }
+ if ((pos + len) > bc_len) {
+ printf(" buffer overflow (opcode=0x%02x)\n", opcode);
+ break;
+ }
+ printf("%s", reopcode_info[opcode].name);
+ switch(opcode) {
+ case REOP_char:
+ val = get_u16(buf + pos + 1);
+ if (val >= ' ' && val <= 126)
+ printf(" '%c'", val);
+ else
+ printf(" 0x%04x", val);
+ break;
+ case REOP_char32:
+ val = get_u32(buf + pos + 1);
+ if (val >= ' ' && val <= 126)
+ printf(" '%c'", val);
+ else
+ printf(" 0x%08x", val);
+ break;
+ case REOP_goto:
+ case REOP_split_goto_first:
+ case REOP_split_next_first:
+ case REOP_loop:
+ case REOP_lookahead:
+ case REOP_negative_lookahead:
+ case REOP_bne_char_pos:
+ val = get_u32(buf + pos + 1);
+ val += (pos + 5);
+ printf(" %u", val);
+ break;
+ case REOP_simple_greedy_quant:
+ printf(" %u %u %u %u",
+ get_u32(buf + pos + 1) + (pos + 17),
+ get_u32(buf + pos + 1 + 4),
+ get_u32(buf + pos + 1 + 8),
+ get_u32(buf + pos + 1 + 12));
+ break;
+ case REOP_save_start:
+ case REOP_save_end:
+ case REOP_back_reference:
+ case REOP_backward_back_reference:
+ printf(" %u", buf[pos + 1]);
+ break;
+ case REOP_save_reset:
+ printf(" %u %u", buf[pos + 1], buf[pos + 2]);
+ break;
+ case REOP_push_i32:
+ val = get_u32(buf + pos + 1);
+ printf(" %d", val);
+ break;
+ case REOP_range:
+ {
+ int n, i;
+ n = get_u16(buf + pos + 1);
+ len += n * 4;
+ for(i = 0; i < n * 2; i++) {
+ val = get_u16(buf + pos + 3 + i * 2);
+ printf(" 0x%04x", val);
+ }
+ }
+ break;
+ case REOP_range32:
+ {
+ int n, i;
+ n = get_u16(buf + pos + 1);
+ len += n * 8;
+ for(i = 0; i < n * 2; i++) {
+ val = get_u32(buf + pos + 3 + i * 4);
+ printf(" 0x%08x", val);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ pos += len;
+ }
+}
+#endif
+
+static void re_emit_op(REParseState *s, int op)
+{
+ dbuf_putc(&s->byte_code, op);
+}
+
+/* return the offset of the u32 value */
+static int re_emit_op_u32(REParseState *s, int op, uint32_t val)
+{
+ int pos;
+ dbuf_putc(&s->byte_code, op);
+ pos = s->byte_code.size;
+ dbuf_put_u32(&s->byte_code, val);
+ return pos;
+}
+
+static int re_emit_goto(REParseState *s, int op, uint32_t val)
+{
+ int pos;
+ dbuf_putc(&s->byte_code, op);
+ pos = s->byte_code.size;
+ dbuf_put_u32(&s->byte_code, val - (pos + 4));
+ return pos;
+}
+
+static void re_emit_op_u8(REParseState *s, int op, uint32_t val)
+{
+ dbuf_putc(&s->byte_code, op);
+ dbuf_putc(&s->byte_code, val);
+}
+
+static void re_emit_op_u16(REParseState *s, int op, uint32_t val)
+{
+ dbuf_putc(&s->byte_code, op);
+ dbuf_put_u16(&s->byte_code, val);
+}
+
+static int FORMAT_ATTR(2, 3) re_parse_error(REParseState *s, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap);
+ va_end(ap);
+ return -1;
+}
+
+static int re_parse_out_of_memory(REParseState *s)
+{
+ return re_parse_error(s, "out of memory");
+}
+
+/* If allow_overflow is false, return -1 in case of
+ overflow. Otherwise return INT32_MAX. */
+static int parse_digits(const uint8_t **pp, BOOL allow_overflow)
+{
+ const uint8_t *p;
+ uint64_t v;
+ int c;
+
+ p = *pp;
+ v = 0;
+ for(;;) {
+ c = *p;
+ if (c < '0' || c > '9')
+ break;
+ v = v * 10 + c - '0';
+ if (v >= INT32_MAX) {
+ if (allow_overflow)
+ v = INT32_MAX;
+ else
+ return -1;
+ }
+ p++;
+ }
+ *pp = p;
+ return v;
+}
+
+static int re_parse_expect(REParseState *s, const uint8_t **pp, int c)
+{
+ const uint8_t *p;
+ p = *pp;
+ if (*p != c)
+ return re_parse_error(s, "expecting '%c'", c);
+ p++;
+ *pp = p;
+ return 0;
+}
+
+/* Parse an escape sequence, *pp points after the '\':
+ allow_utf16 value:
+ 0 : no UTF-16 escapes allowed
+ 1 : UTF-16 escapes allowed
+ 2 : UTF-16 escapes allowed and escapes of surrogate pairs are
+ converted to a unicode character (unicode regexp case).
+
+ Return the unicode char and update *pp if recognized,
+ return -1 if malformed escape,
+ return -2 otherwise. */
+int lre_parse_escape(const uint8_t **pp, int allow_utf16)
+{
+ const uint8_t *p;
+ uint32_t c;
+
+ p = *pp;
+ c = *p++;
+ switch(c) {
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 'x':
+ case 'u':
+ {
+ int h, n, i;
+ uint32_t c1;
+
+ if (*p == '{' && allow_utf16) {
+ p++;
+ c = 0;
+ for(;;) {
+ h = from_hex(*p++);
+ if (h < 0)
+ return -1;
+ c = (c << 4) | h;
+ if (c > 0x10FFFF)
+ return -1;
+ if (*p == '}')
+ break;
+ }
+ p++;
+ } else {
+ if (c == 'x') {
+ n = 2;
+ } else {
+ n = 4;
+ }
+
+ c = 0;
+ for(i = 0; i < n; i++) {
+ h = from_hex(*p++);
+ if (h < 0) {
+ return -1;
+ }
+ c = (c << 4) | h;
+ }
+ if (c >= 0xd800 && c < 0xdc00 &&
+ allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') {
+ /* convert an escaped surrogate pair into a
+ unicode char */
+ c1 = 0;
+ for(i = 0; i < 4; i++) {
+ h = from_hex(p[2 + i]);
+ if (h < 0)
+ break;
+ c1 = (c1 << 4) | h;
+ }
+ if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) {
+ p += 6;
+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
+ }
+ }
+ }
+ }
+ break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c -= '0';
+ if (allow_utf16 == 2) {
+ /* only accept \0 not followed by digit */
+ if (c != 0 || is_digit(*p))
+ return -1;
+ } else {
+ /* parse a legacy octal sequence */
+ uint32_t v;
+ v = *p - '0';
+ if (v > 7)
+ break;
+ c = (c << 3) | v;
+ p++;
+ if (c >= 32)
+ break;
+ v = *p - '0';
+ if (v > 7)
+ break;
+ c = (c << 3) | v;
+ p++;
+ }
+ break;
+ default:
+ return -2;
+ }
+ *pp = p;
+ return c;
+}
+
+#ifdef CONFIG_ALL_UNICODE
+/* XXX: we use the same chars for name and value */
+static BOOL is_unicode_char(int c)
+{
+ return ((c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c == '_'));
+}
+
+static int parse_unicode_property(REParseState *s, CharRange *cr,
+ const uint8_t **pp, BOOL is_inv)
+{
+ const uint8_t *p;
+ char name[64], value[64];
+ char *q;
+ BOOL script_ext;
+ int ret;
+
+ p = *pp;
+ if (*p != '{')
+ return re_parse_error(s, "expecting '{' after \\p");
+ p++;
+ q = name;
+ while (is_unicode_char(*p)) {
+ if ((q - name) >= sizeof(name) - 1)
+ goto unknown_property_name;
+ *q++ = *p++;
+ }
+ *q = '\0';
+ q = value;
+ if (*p == '=') {
+ p++;
+ while (is_unicode_char(*p)) {
+ if ((q - value) >= sizeof(value) - 1)
+ return re_parse_error(s, "unknown unicode property value");
+ *q++ = *p++;
+ }
+ }
+ *q = '\0';
+ if (*p != '}')
+ return re_parse_error(s, "expecting '}'");
+ p++;
+ // printf("name=%s value=%s\n", name, value);
+
+ if (!strcmp(name, "Script") || !strcmp(name, "sc")) {
+ script_ext = FALSE;
+ goto do_script;
+ } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) {
+ script_ext = TRUE;
+ do_script:
+ cr_init(cr, s->opaque, lre_realloc);
+ ret = unicode_script(cr, value, script_ext);
+ if (ret) {
+ cr_free(cr);
+ if (ret == -2)
+ return re_parse_error(s, "unknown unicode script");
+ else
+ goto out_of_memory;
+ }
+ } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) {
+ cr_init(cr, s->opaque, lre_realloc);
+ ret = unicode_general_category(cr, value);
+ if (ret) {
+ cr_free(cr);
+ if (ret == -2)
+ return re_parse_error(s, "unknown unicode general category");
+ else
+ goto out_of_memory;
+ }
+ } else if (value[0] == '\0') {
+ cr_init(cr, s->opaque, lre_realloc);
+ ret = unicode_general_category(cr, name);
+ if (ret == -1) {
+ cr_free(cr);
+ goto out_of_memory;
+ }
+ if (ret < 0) {
+ ret = unicode_prop(cr, name);
+ if (ret) {
+ cr_free(cr);
+ if (ret == -2)
+ goto unknown_property_name;
+ else
+ goto out_of_memory;
+ }
+ }
+ } else {
+ unknown_property_name:
+ return re_parse_error(s, "unknown unicode property name");
+ }
+
+ if (is_inv) {
+ if (cr_invert(cr)) {
+ cr_free(cr);
+ return -1;
+ }
+ }
+ *pp = p;
+ return 0;
+ out_of_memory:
+ return re_parse_out_of_memory(s);
+}
+#endif /* CONFIG_ALL_UNICODE */
+
+/* return -1 if error otherwise the character or a class range
+ (CLASS_RANGE_BASE). In case of class range, 'cr' is
+ initialized. Otherwise, it is ignored. */
+static int get_class_atom(REParseState *s, CharRange *cr,
+ const uint8_t **pp, BOOL inclass)
+{
+ const uint8_t *p;
+ uint32_t c;
+ int ret;
+
+ p = *pp;
+
+ c = *p;
+ switch(c) {
+ case '\\':
+ p++;
+ if (p >= s->buf_end)
+ goto unexpected_end;
+ c = *p++;
+ switch(c) {
+ case 'd':
+ c = CHAR_RANGE_d;
+ goto class_range;
+ case 'D':
+ c = CHAR_RANGE_D;
+ goto class_range;
+ case 's':
+ c = CHAR_RANGE_s;
+ goto class_range;
+ case 'S':
+ c = CHAR_RANGE_S;
+ goto class_range;
+ case 'w':
+ c = CHAR_RANGE_w;
+ goto class_range;
+ case 'W':
+ c = CHAR_RANGE_W;
+ class_range:
+ if (cr_init_char_range(s, cr, c))
+ return -1;
+ c = CLASS_RANGE_BASE;
+ break;
+ case 'c':
+ c = *p;
+ if ((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (((c >= '0' && c <= '9') || c == '_') &&
+ inclass && !s->is_utf16)) { /* Annex B.1.4 */
+ c &= 0x1f;
+ p++;
+ } else if (s->is_utf16) {
+ goto invalid_escape;
+ } else {
+ /* otherwise return '\' and 'c' */
+ p--;
+ c = '\\';
+ }
+ break;
+#ifdef CONFIG_ALL_UNICODE
+ case 'p':
+ case 'P':
+ if (s->is_utf16) {
+ if (parse_unicode_property(s, cr, &p, (c == 'P')))
+ return -1;
+ c = CLASS_RANGE_BASE;
+ break;
+ }
+ /* fall thru */
+#endif
+ default:
+ p--;
+ ret = lre_parse_escape(&p, s->is_utf16 * 2);
+ if (ret >= 0) {
+ c = ret;
+ } else {
+ if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) {
+ /* always valid to escape these characters */
+ goto normal_char;
+ } else if (s->is_utf16) {
+ invalid_escape:
+ return re_parse_error(s, "invalid escape sequence in regular expression");
+ } else {
+ /* just ignore the '\' */
+ goto normal_char;
+ }
+ }
+ break;
+ }
+ break;
+ case '\0':
+ if (p >= s->buf_end) {
+ unexpected_end:
+ return re_parse_error(s, "unexpected end");
+ }
+ /* fall thru */
+ default:
+ normal_char:
+ /* normal char */
+ if (c >= 128) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if ((unsigned)c > 0xffff && !s->is_utf16) {
+ /* XXX: should handle non BMP-1 code points */
+ return re_parse_error(s, "malformed unicode char");
+ }
+ } else {
+ p++;
+ }
+ break;
+ }
+ *pp = p;
+ return c;
+}
+
+static int re_emit_range(REParseState *s, const CharRange *cr)
+{
+ int len, i;
+ uint32_t high;
+
+ len = (unsigned)cr->len / 2;
+ if (len >= 65535)
+ return re_parse_error(s, "too many ranges");
+ if (len == 0) {
+ /* not sure it can really happen. Emit a match that is always
+ false */
+ re_emit_op_u32(s, REOP_char32, -1);
+ } else {
+ high = cr->points[cr->len - 1];
+ if (high == UINT32_MAX)
+ high = cr->points[cr->len - 2];
+ if (high <= 0xffff) {
+ /* can use 16 bit ranges with the conversion that 0xffff =
+ infinity */
+ re_emit_op_u16(s, REOP_range, len);
+ for(i = 0; i < cr->len; i += 2) {
+ dbuf_put_u16(&s->byte_code, cr->points[i]);
+ high = cr->points[i + 1] - 1;
+ if (high == UINT32_MAX - 1)
+ high = 0xffff;
+ dbuf_put_u16(&s->byte_code, high);
+ }
+ } else {
+ re_emit_op_u16(s, REOP_range32, len);
+ for(i = 0; i < cr->len; i += 2) {
+ dbuf_put_u32(&s->byte_code, cr->points[i]);
+ dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1);
+ }
+ }
+ }
+ return 0;
+}
+
+static int re_parse_char_class(REParseState *s, const uint8_t **pp)
+{
+ const uint8_t *p;
+ uint32_t c1, c2;
+ CharRange cr_s, *cr = &cr_s;
+ CharRange cr1_s, *cr1 = &cr1_s;
+ BOOL invert;
+
+ cr_init(cr, s->opaque, lre_realloc);
+ p = *pp;
+ p++; /* skip '[' */
+ invert = FALSE;
+ if (*p == '^') {
+ p++;
+ invert = TRUE;
+ }
+ for(;;) {
+ if (*p == ']')
+ break;
+ c1 = get_class_atom(s, cr1, &p, TRUE);
+ if ((int)c1 < 0)
+ goto fail;
+ if (*p == '-' && p[1] != ']') {
+ const uint8_t *p0 = p + 1;
+ if (c1 >= CLASS_RANGE_BASE) {
+ if (s->is_utf16) {
+ cr_free(cr1);
+ goto invalid_class_range;
+ }
+ /* Annex B: match '-' character */
+ goto class_atom;
+ }
+ c2 = get_class_atom(s, cr1, &p0, TRUE);
+ if ((int)c2 < 0)
+ goto fail;
+ if (c2 >= CLASS_RANGE_BASE) {
+ cr_free(cr1);
+ if (s->is_utf16) {
+ goto invalid_class_range;
+ }
+ /* Annex B: match '-' character */
+ goto class_atom;
+ }
+ p = p0;
+ if (c2 < c1) {
+ invalid_class_range:
+ re_parse_error(s, "invalid class range");
+ goto fail;
+ }
+ if (cr_union_interval(cr, c1, c2))
+ goto memory_error;
+ } else {
+ class_atom:
+ if (c1 >= CLASS_RANGE_BASE) {
+ int ret;
+ ret = cr_union1(cr, cr1->points, cr1->len);
+ cr_free(cr1);
+ if (ret)
+ goto memory_error;
+ } else {
+ if (cr_union_interval(cr, c1, c1))
+ goto memory_error;
+ }
+ }
+ }
+ if (s->ignore_case) {
+ if (cr_canonicalize(cr))
+ goto memory_error;
+ }
+ if (invert) {
+ if (cr_invert(cr))
+ goto memory_error;
+ }
+ if (re_emit_range(s, cr))
+ goto fail;
+ cr_free(cr);
+ p++; /* skip ']' */
+ *pp = p;
+ return 0;
+ memory_error:
+ re_parse_out_of_memory(s);
+ fail:
+ cr_free(cr);
+ return -1;
+}
+
+/* Return:
+ 1 if the opcodes in bc_buf[] always advance the character pointer.
+ 0 if the character pointer may not be advanced.
+ -1 if the code may depend on side effects of its previous execution (backreference)
+*/
+static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len)
+{
+ int pos, opcode, ret, len, i;
+ uint32_t val, last;
+ BOOL has_back_reference;
+ uint8_t capture_bitmap[CAPTURE_COUNT_MAX];
+
+ ret = -2; /* not known yet */
+ pos = 0;
+ has_back_reference = FALSE;
+ memset(capture_bitmap, 0, sizeof(capture_bitmap));
+
+ while (pos < bc_buf_len) {
+ opcode = bc_buf[pos];
+ len = reopcode_info[opcode].size;
+ switch(opcode) {
+ case REOP_range:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 4;
+ goto simple_char;
+ case REOP_range32:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 8;
+ goto simple_char;
+ case REOP_char:
+ case REOP_char32:
+ case REOP_dot:
+ case REOP_any:
+ simple_char:
+ if (ret == -2)
+ ret = 1;
+ break;
+ case REOP_line_start:
+ case REOP_line_end:
+ case REOP_push_i32:
+ case REOP_push_char_pos:
+ case REOP_drop:
+ case REOP_word_boundary:
+ case REOP_not_word_boundary:
+ case REOP_prev:
+ /* no effect */
+ break;
+ case REOP_save_start:
+ case REOP_save_end:
+ val = bc_buf[pos + 1];
+ capture_bitmap[val] |= 1;
+ break;
+ case REOP_save_reset:
+ {
+ val = bc_buf[pos + 1];
+ last = bc_buf[pos + 2];
+ while (val < last)
+ capture_bitmap[val++] |= 1;
+ }
+ break;
+ case REOP_back_reference:
+ case REOP_backward_back_reference:
+ val = bc_buf[pos + 1];
+ capture_bitmap[val] |= 2;
+ has_back_reference = TRUE;
+ break;
+ default:
+ /* safe behvior: we cannot predict the outcome */
+ if (ret == -2)
+ ret = 0;
+ break;
+ }
+ pos += len;
+ }
+ if (has_back_reference) {
+ /* check if there is back reference which references a capture
+ made in the some code */
+ for(i = 0; i < CAPTURE_COUNT_MAX; i++) {
+ if (capture_bitmap[i] == 3)
+ return -1;
+ }
+ }
+ if (ret == -2)
+ ret = 0;
+ return ret;
+}
+
+/* return -1 if a simple quantifier cannot be used. Otherwise return
+ the number of characters in the atom. */
+static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len)
+{
+ int pos, opcode, len, count;
+ uint32_t val;
+
+ count = 0;
+ pos = 0;
+ while (pos < bc_buf_len) {
+ opcode = bc_buf[pos];
+ len = reopcode_info[opcode].size;
+ switch(opcode) {
+ case REOP_range:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 4;
+ goto simple_char;
+ case REOP_range32:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 8;
+ goto simple_char;
+ case REOP_char:
+ case REOP_char32:
+ case REOP_dot:
+ case REOP_any:
+ simple_char:
+ count++;
+ break;
+ case REOP_line_start:
+ case REOP_line_end:
+ case REOP_word_boundary:
+ case REOP_not_word_boundary:
+ break;
+ default:
+ return -1;
+ }
+ pos += len;
+ }
+ return count;
+}
+
+/* '*pp' is the first char after '<' */
+static int re_parse_group_name(char *buf, int buf_size,
+ const uint8_t **pp, BOOL is_utf16)
+{
+ const uint8_t *p;
+ uint32_t c;
+ char *q;
+
+ p = *pp;
+ q = buf;
+ for(;;) {
+ c = *p;
+ if (c == '\\') {
+ p++;
+ if (*p != 'u')
+ return -1;
+ c = lre_parse_escape(&p, is_utf16 * 2);
+ } else if (c == '>') {
+ break;
+ } else if (c >= 128) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ } else {
+ p++;
+ }
+ if (c > 0x10FFFF)
+ return -1;
+ if (q == buf) {
+ if (!lre_js_is_ident_first(c))
+ return -1;
+ } else {
+ if (!lre_js_is_ident_next(c))
+ return -1;
+ }
+ if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size)
+ return -1;
+ if (c < 128) {
+ *q++ = c;
+ } else {
+ q += unicode_to_utf8((uint8_t*)q, c);
+ }
+ }
+ if (q == buf)
+ return -1;
+ *q = '\0';
+ p++;
+ *pp = p;
+ return 0;
+}
+
+/* if capture_name = NULL: return the number of captures + 1.
+ Otherwise, return the capture index corresponding to capture_name
+ or -1 if none */
+static int re_parse_captures(REParseState *s, int *phas_named_captures,
+ const char *capture_name)
+{
+ const uint8_t *p;
+ int capture_index;
+ char name[TMP_BUF_SIZE];
+
+ capture_index = 1;
+ *phas_named_captures = 0;
+ for (p = s->buf_start; p < s->buf_end; p++) {
+ switch (*p) {
+ case '(':
+ if (p[1] == '?') {
+ if (p[2] == '<' && p[3] != '=' && p[3] != '!') {
+ *phas_named_captures = 1;
+ /* potential named capture */
+ if (capture_name) {
+ p += 3;
+ if (re_parse_group_name(name, sizeof(name), &p,
+ s->is_utf16) == 0) {
+ if (!strcmp(name, capture_name))
+ return capture_index;
+ }
+ }
+ capture_index++;
+ if (capture_index >= CAPTURE_COUNT_MAX)
+ goto done;
+ }
+ } else {
+ capture_index++;
+ if (capture_index >= CAPTURE_COUNT_MAX)
+ goto done;
+ }
+ break;
+ case '\\':
+ p++;
+ break;
+ case '[':
+ for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) {
+ if (*p == '\\')
+ p++;
+ }
+ break;
+ }
+ }
+ done:
+ if (capture_name)
+ return -1;
+ else
+ return capture_index;
+}
+
+static int re_count_captures(REParseState *s)
+{
+ if (s->total_capture_count < 0) {
+ s->total_capture_count = re_parse_captures(s, &s->has_named_captures,
+ NULL);
+ }
+ return s->total_capture_count;
+}
+
+static BOOL re_has_named_captures(REParseState *s)
+{
+ if (s->has_named_captures < 0)
+ re_count_captures(s);
+ return s->has_named_captures;
+}
+
+static int find_group_name(REParseState *s, const char *name)
+{
+ const char *p, *buf_end;
+ size_t len, name_len;
+ int capture_index;
+
+ name_len = strlen(name);
+ p = (char *)s->group_names.buf;
+ buf_end = (char *)s->group_names.buf + s->group_names.size;
+ capture_index = 1;
+ while (p < buf_end) {
+ len = strlen(p);
+ if (len == name_len && memcmp(name, p, name_len) == 0)
+ return capture_index;
+ p += len + 1;
+ capture_index++;
+ }
+ return -1;
+}
+
+static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir);
+
+static int re_parse_term(REParseState *s, BOOL is_backward_dir)
+{
+ const uint8_t *p;
+ int c, last_atom_start, quant_min, quant_max, last_capture_count;
+ BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead;
+ CharRange cr_s, *cr = &cr_s;
+
+ last_atom_start = -1;
+ last_capture_count = 0;
+ p = s->buf_ptr;
+ c = *p;
+ switch(c) {
+ case '^':
+ p++;
+ re_emit_op(s, REOP_line_start);
+ break;
+ case '$':
+ p++;
+ re_emit_op(s, REOP_line_end);
+ break;
+ case '.':
+ p++;
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ re_emit_op(s, s->dotall ? REOP_any : REOP_dot);
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ break;
+ case '{':
+ if (s->is_utf16) {
+ return re_parse_error(s, "syntax error");
+ } else if (!is_digit(p[1])) {
+ /* Annex B: we accept '{' not followed by digits as a
+ normal atom */
+ goto parse_class_atom;
+ } else {
+ const uint8_t *p1 = p + 1;
+ /* Annex B: error if it is like a repetition count */
+ parse_digits(&p1, TRUE);
+ if (*p1 == ',') {
+ p1++;
+ if (is_digit(*p1)) {
+ parse_digits(&p1, TRUE);
+ }
+ }
+ if (*p1 != '}') {
+ goto parse_class_atom;
+ }
+ }
+ /* fall thru */
+ case '*':
+ case '+':
+ case '?':
+ return re_parse_error(s, "nothing to repeat");
+ case '(':
+ if (p[1] == '?') {
+ if (p[2] == ':') {
+ p += 3;
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ s->buf_ptr = p;
+ if (re_parse_disjunction(s, is_backward_dir))
+ return -1;
+ p = s->buf_ptr;
+ if (re_parse_expect(s, &p, ')'))
+ return -1;
+ } else if ((p[2] == '=' || p[2] == '!')) {
+ is_neg = (p[2] == '!');
+ is_backward_lookahead = FALSE;
+ p += 3;
+ goto lookahead;
+ } else if (p[2] == '<' &&
+ (p[3] == '=' || p[3] == '!')) {
+ int pos;
+ is_neg = (p[3] == '!');
+ is_backward_lookahead = TRUE;
+ p += 4;
+ /* lookahead */
+ lookahead:
+ /* Annex B allows lookahead to be used as an atom for
+ the quantifiers */
+ if (!s->is_utf16 && !is_backward_lookahead) {
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ }
+ pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0);
+ s->buf_ptr = p;
+ if (re_parse_disjunction(s, is_backward_lookahead))
+ return -1;
+ p = s->buf_ptr;
+ if (re_parse_expect(s, &p, ')'))
+ return -1;
+ re_emit_op(s, REOP_match);
+ /* jump after the 'match' after the lookahead is successful */
+ if (dbuf_error(&s->byte_code))
+ return -1;
+ put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4));
+ } else if (p[2] == '<') {
+ p += 3;
+ if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
+ &p, s->is_utf16)) {
+ return re_parse_error(s, "invalid group name");
+ }
+ if (find_group_name(s, s->u.tmp_buf) > 0) {
+ return re_parse_error(s, "duplicate group name");
+ }
+ /* group name with a trailing zero */
+ dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf,
+ strlen(s->u.tmp_buf) + 1);
+ s->has_named_captures = 1;
+ goto parse_capture;
+ } else {
+ return re_parse_error(s, "invalid group");
+ }
+ } else {
+ int capture_index;
+ p++;
+ /* capture without group name */
+ dbuf_putc(&s->group_names, 0);
+ parse_capture:
+ if (s->capture_count >= CAPTURE_COUNT_MAX)
+ return re_parse_error(s, "too many captures");
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ capture_index = s->capture_count++;
+ re_emit_op_u8(s, REOP_save_start + is_backward_dir,
+ capture_index);
+
+ s->buf_ptr = p;
+ if (re_parse_disjunction(s, is_backward_dir))
+ return -1;
+ p = s->buf_ptr;
+
+ re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir,
+ capture_index);
+
+ if (re_parse_expect(s, &p, ')'))
+ return -1;
+ }
+ break;
+ case '\\':
+ switch(p[1]) {
+ case 'b':
+ case 'B':
+ re_emit_op(s, REOP_word_boundary + (p[1] != 'b'));
+ p += 2;
+ break;
+ case 'k':
+ {
+ const uint8_t *p1;
+ int dummy_res;
+
+ p1 = p;
+ if (p1[2] != '<') {
+ /* annex B: we tolerate invalid group names in non
+ unicode mode if there is no named capture
+ definition */
+ if (s->is_utf16 || re_has_named_captures(s))
+ return re_parse_error(s, "expecting group name");
+ else
+ goto parse_class_atom;
+ }
+ p1 += 3;
+ if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
+ &p1, s->is_utf16)) {
+ if (s->is_utf16 || re_has_named_captures(s))
+ return re_parse_error(s, "invalid group name");
+ else
+ goto parse_class_atom;
+ }
+ c = find_group_name(s, s->u.tmp_buf);
+ if (c < 0) {
+ /* no capture name parsed before, try to look
+ after (inefficient, but hopefully not common */
+ c = re_parse_captures(s, &dummy_res, s->u.tmp_buf);
+ if (c < 0) {
+ if (s->is_utf16 || re_has_named_captures(s))
+ return re_parse_error(s, "group name not defined");
+ else
+ goto parse_class_atom;
+ }
+ }
+ p = p1;
+ }
+ goto emit_back_reference;
+ case '0':
+ p += 2;
+ c = 0;
+ if (s->is_utf16) {
+ if (is_digit(*p)) {
+ return re_parse_error(s, "invalid decimal escape in regular expression");
+ }
+ } else {
+ /* Annex B.1.4: accept legacy octal */
+ if (*p >= '0' && *p <= '7') {
+ c = *p++ - '0';
+ if (*p >= '0' && *p <= '7') {
+ c = (c << 3) + *p++ - '0';
+ }
+ }
+ }
+ goto normal_char;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
+ {
+ const uint8_t *q = ++p;
+
+ c = parse_digits(&p, FALSE);
+ if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) {
+ if (!s->is_utf16) {
+ /* Annex B.1.4: accept legacy octal */
+ p = q;
+ if (*p <= '7') {
+ c = 0;
+ if (*p <= '3')
+ c = *p++ - '0';
+ if (*p >= '0' && *p <= '7') {
+ c = (c << 3) + *p++ - '0';
+ if (*p >= '0' && *p <= '7') {
+ c = (c << 3) + *p++ - '0';
+ }
+ }
+ } else {
+ c = *p++;
+ }
+ goto normal_char;
+ }
+ return re_parse_error(s, "back reference out of range in regular expression");
+ }
+ emit_back_reference:
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ re_emit_op_u8(s, REOP_back_reference + is_backward_dir, c);
+ }
+ break;
+ default:
+ goto parse_class_atom;
+ }
+ break;
+ case '[':
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ if (re_parse_char_class(s, &p))
+ return -1;
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ break;
+ case ']':
+ case '}':
+ if (s->is_utf16)
+ return re_parse_error(s, "syntax error");
+ goto parse_class_atom;
+ default:
+ parse_class_atom:
+ c = get_class_atom(s, cr, &p, FALSE);
+ if (c < 0)
+ return -1;
+ normal_char:
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ if (c >= CLASS_RANGE_BASE) {
+ int ret;
+ /* Note: canonicalization is not needed */
+ ret = re_emit_range(s, cr);
+ cr_free(cr);
+ if (ret)
+ return -1;
+ } else {
+ if (s->ignore_case)
+ c = lre_canonicalize(c, s->is_utf16);
+ if (c <= 0xffff)
+ re_emit_op_u16(s, REOP_char, c);
+ else
+ re_emit_op_u32(s, REOP_char32, c);
+ }
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ break;
+ }
+
+ /* quantifier */
+ if (last_atom_start >= 0) {
+ c = *p;
+ switch(c) {
+ case '*':
+ p++;
+ quant_min = 0;
+ quant_max = INT32_MAX;
+ goto quantifier;
+ case '+':
+ p++;
+ quant_min = 1;
+ quant_max = INT32_MAX;
+ goto quantifier;
+ case '?':
+ p++;
+ quant_min = 0;
+ quant_max = 1;
+ goto quantifier;
+ case '{':
+ {
+ const uint8_t *p1 = p;
+ /* As an extension (see ES6 annex B), we accept '{' not
+ followed by digits as a normal atom */
+ if (!is_digit(p[1])) {
+ if (s->is_utf16)
+ goto invalid_quant_count;
+ break;
+ }
+ p++;
+ quant_min = parse_digits(&p, TRUE);
+ quant_max = quant_min;
+ if (*p == ',') {
+ p++;
+ if (is_digit(*p)) {
+ quant_max = parse_digits(&p, TRUE);
+ if (quant_max < quant_min) {
+ invalid_quant_count:
+ return re_parse_error(s, "invalid repetition count");
+ }
+ } else {
+ quant_max = INT32_MAX; /* infinity */
+ }
+ }
+ if (*p != '}' && !s->is_utf16) {
+ /* Annex B: normal atom if invalid '{' syntax */
+ p = p1;
+ break;
+ }
+ if (re_parse_expect(s, &p, '}'))
+ return -1;
+ }
+ quantifier:
+ greedy = TRUE;
+ if (*p == '?') {
+ p++;
+ greedy = FALSE;
+ }
+ if (last_atom_start < 0) {
+ return re_parse_error(s, "nothing to repeat");
+ }
+ if (greedy) {
+ int len, pos;
+
+ if (quant_max > 0) {
+ /* specific optimization for simple quantifiers */
+ if (dbuf_error(&s->byte_code))
+ goto out_of_memory;
+ len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start,
+ s->byte_code.size - last_atom_start);
+ if (len > 0) {
+ re_emit_op(s, REOP_match);
+
+ if (dbuf_insert(&s->byte_code, last_atom_start, 17))
+ goto out_of_memory;
+ pos = last_atom_start;
+ s->byte_code.buf[pos++] = REOP_simple_greedy_quant;
+ put_u32(&s->byte_code.buf[pos],
+ s->byte_code.size - last_atom_start - 17);
+ pos += 4;
+ put_u32(&s->byte_code.buf[pos], quant_min);
+ pos += 4;
+ put_u32(&s->byte_code.buf[pos], quant_max);
+ pos += 4;
+ put_u32(&s->byte_code.buf[pos], len);
+ pos += 4;
+ goto done;
+ }
+ }
+
+ if (dbuf_error(&s->byte_code))
+ goto out_of_memory;
+ add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start,
+ s->byte_code.size - last_atom_start) == 0);
+ } else {
+ add_zero_advance_check = FALSE;
+ }
+
+ {
+ int len, pos;
+ len = s->byte_code.size - last_atom_start;
+ if (quant_min == 0) {
+ /* need to reset the capture in case the atom is
+ not executed */
+ if (last_capture_count != s->capture_count) {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 3))
+ goto out_of_memory;
+ s->byte_code.buf[last_atom_start++] = REOP_save_reset;
+ s->byte_code.buf[last_atom_start++] = last_capture_count;
+ s->byte_code.buf[last_atom_start++] = s->capture_count - 1;
+ }
+ if (quant_max == 0) {
+ s->byte_code.size = last_atom_start;
+ } else if (quant_max == 1) {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 5))
+ goto out_of_memory;
+ s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
+ greedy;
+ put_u32(s->byte_code.buf + last_atom_start + 1, len);
+ } else if (quant_max == INT32_MAX) {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check))
+ goto out_of_memory;
+ s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
+ greedy;
+ put_u32(s->byte_code.buf + last_atom_start + 1,
+ len + 5 + add_zero_advance_check);
+ if (add_zero_advance_check) {
+ /* avoid infinite loop by stoping the
+ recursion if no advance was made in the
+ atom (only works if the atom has no
+ side effect) */
+ s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos;
+ re_emit_goto(s, REOP_bne_char_pos, last_atom_start);
+ } else {
+ re_emit_goto(s, REOP_goto, last_atom_start);
+ }
+ } else {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 10))
+ goto out_of_memory;
+ pos = last_atom_start;
+ s->byte_code.buf[pos++] = REOP_push_i32;
+ put_u32(s->byte_code.buf + pos, quant_max);
+ pos += 4;
+ s->byte_code.buf[pos++] = REOP_split_goto_first + greedy;
+ put_u32(s->byte_code.buf + pos, len + 5);
+ re_emit_goto(s, REOP_loop, last_atom_start + 5);
+ re_emit_op(s, REOP_drop);
+ }
+ } else if (quant_min == 1 && quant_max == INT32_MAX &&
+ !add_zero_advance_check) {
+ re_emit_goto(s, REOP_split_next_first - greedy,
+ last_atom_start);
+ } else {
+ if (quant_min == 1) {
+ /* nothing to add */
+ } else {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 5))
+ goto out_of_memory;
+ s->byte_code.buf[last_atom_start] = REOP_push_i32;
+ put_u32(s->byte_code.buf + last_atom_start + 1,
+ quant_min);
+ last_atom_start += 5;
+ re_emit_goto(s, REOP_loop, last_atom_start);
+ re_emit_op(s, REOP_drop);
+ }
+ if (quant_max == INT32_MAX) {
+ pos = s->byte_code.size;
+ re_emit_op_u32(s, REOP_split_goto_first + greedy,
+ len + 5 + add_zero_advance_check);
+ if (add_zero_advance_check)
+ re_emit_op(s, REOP_push_char_pos);
+ /* copy the atom */
+ dbuf_put_self(&s->byte_code, last_atom_start, len);
+ if (add_zero_advance_check)
+ re_emit_goto(s, REOP_bne_char_pos, pos);
+ else
+ re_emit_goto(s, REOP_goto, pos);
+ } else if (quant_max > quant_min) {
+ re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min);
+ pos = s->byte_code.size;
+ re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5);
+ /* copy the atom */
+ dbuf_put_self(&s->byte_code, last_atom_start, len);
+
+ re_emit_goto(s, REOP_loop, pos);
+ re_emit_op(s, REOP_drop);
+ }
+ }
+ last_atom_start = -1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ done:
+ s->buf_ptr = p;
+ return 0;
+ out_of_memory:
+ return re_parse_out_of_memory(s);
+}
+
+static int re_parse_alternative(REParseState *s, BOOL is_backward_dir)
+{
+ const uint8_t *p;
+ int ret;
+ size_t start, term_start, end, term_size;
+
+ start = s->byte_code.size;
+ for(;;) {
+ p = s->buf_ptr;
+ if (p >= s->buf_end)
+ break;
+ if (*p == '|' || *p == ')')
+ break;
+ term_start = s->byte_code.size;
+ ret = re_parse_term(s, is_backward_dir);
+ if (ret)
+ return ret;
+ if (is_backward_dir) {
+ /* reverse the order of the terms (XXX: inefficient, but
+ speed is not really critical here) */
+ end = s->byte_code.size;
+ term_size = end - term_start;
+ if (dbuf_realloc(&s->byte_code, end + term_size))
+ return -1;
+ memmove(s->byte_code.buf + start + term_size,
+ s->byte_code.buf + start,
+ end - start);
+ memcpy(s->byte_code.buf + start, s->byte_code.buf + end,
+ term_size);
+ }
+ }
+ return 0;
+}
+
+static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)
+{
+ int start, len, pos;
+
+ if (lre_check_stack_overflow(s->opaque, 0))
+ return re_parse_error(s, "stack overflow");
+
+ start = s->byte_code.size;
+ if (re_parse_alternative(s, is_backward_dir))
+ return -1;
+ while (*s->buf_ptr == '|') {
+ s->buf_ptr++;
+
+ len = s->byte_code.size - start;
+
+ /* insert a split before the first alternative */
+ if (dbuf_insert(&s->byte_code, start, 5)) {
+ return re_parse_out_of_memory(s);
+ }
+ s->byte_code.buf[start] = REOP_split_next_first;
+ put_u32(s->byte_code.buf + start + 1, len + 5);
+
+ pos = re_emit_op_u32(s, REOP_goto, 0);
+
+ if (re_parse_alternative(s, is_backward_dir))
+ return -1;
+
+ /* patch the goto */
+ len = s->byte_code.size - (pos + 4);
+ put_u32(s->byte_code.buf + pos, len);
+ }
+ return 0;
+}
+
+/* the control flow is recursive so the analysis can be linear */
+static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len)
+{
+ int stack_size, stack_size_max, pos, opcode, len;
+ uint32_t val;
+
+ stack_size = 0;
+ stack_size_max = 0;
+ bc_buf += RE_HEADER_LEN;
+ bc_buf_len -= RE_HEADER_LEN;
+ pos = 0;
+ while (pos < bc_buf_len) {
+ opcode = bc_buf[pos];
+ len = reopcode_info[opcode].size;
+ assert(opcode < REOP_COUNT);
+ assert((pos + len) <= bc_buf_len);
+ switch(opcode) {
+ case REOP_push_i32:
+ case REOP_push_char_pos:
+ stack_size++;
+ if (stack_size > stack_size_max) {
+ if (stack_size > STACK_SIZE_MAX)
+ return -1;
+ stack_size_max = stack_size;
+ }
+ break;
+ case REOP_drop:
+ case REOP_bne_char_pos:
+ assert(stack_size > 0);
+ stack_size--;
+ break;
+ case REOP_range:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 4;
+ break;
+ case REOP_range32:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 8;
+ break;
+ }
+ pos += len;
+ }
+ return stack_size_max;
+}
+
+/* 'buf' must be a zero terminated UTF-8 string of length buf_len.
+ Return NULL if error and allocate an error message in *perror_msg,
+ otherwise the compiled bytecode and its length in plen.
+*/
+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)
+{
+ REParseState s_s, *s = &s_s;
+ int stack_size;
+ BOOL is_sticky;
+
+ memset(s, 0, sizeof(*s));
+ s->opaque = opaque;
+ s->buf_ptr = (const uint8_t *)buf;
+ s->buf_end = s->buf_ptr + buf_len;
+ s->buf_start = s->buf_ptr;
+ s->re_flags = re_flags;
+ s->is_utf16 = ((re_flags & LRE_FLAG_UTF16) != 0);
+ is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0);
+ s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0);
+ s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0);
+ s->capture_count = 1;
+ s->total_capture_count = -1;
+ s->has_named_captures = -1;
+
+ dbuf_init2(&s->byte_code, opaque, lre_realloc);
+ dbuf_init2(&s->group_names, opaque, lre_realloc);
+
+ dbuf_putc(&s->byte_code, re_flags); /* first element is the flags */
+ dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */
+ dbuf_putc(&s->byte_code, 0); /* stack size */
+ dbuf_put_u32(&s->byte_code, 0); /* bytecode length */
+
+ if (!is_sticky) {
+ /* iterate thru all positions (about the same as .*?( ... ) )
+ . We do it without an explicit loop so that lock step
+ thread execution will be possible in an optimized
+ implementation */
+ re_emit_op_u32(s, REOP_split_goto_first, 1 + 5);
+ re_emit_op(s, REOP_any);
+ re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5));
+ }
+ re_emit_op_u8(s, REOP_save_start, 0);
+
+ if (re_parse_disjunction(s, FALSE)) {
+ error:
+ dbuf_free(&s->byte_code);
+ dbuf_free(&s->group_names);
+ pstrcpy(error_msg, error_msg_size, s->u.error_msg);
+ *plen = 0;
+ return NULL;
+ }
+
+ re_emit_op_u8(s, REOP_save_end, 0);
+
+ re_emit_op(s, REOP_match);
+
+ if (*s->buf_ptr != '\0') {
+ re_parse_error(s, "extraneous characters at the end");
+ goto error;
+ }
+
+ if (dbuf_error(&s->byte_code)) {
+ re_parse_out_of_memory(s);
+ goto error;
+ }
+
+ stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size);
+ if (stack_size < 0) {
+ re_parse_error(s, "too many imbricated quantifiers");
+ goto error;
+ }
+
+ s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count;
+ s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size;
+ put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_LEN);
+
+ /* add the named groups if needed */
+ if (s->group_names.size > (s->capture_count - 1)) {
+ dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size);
+ s->byte_code.buf[RE_HEADER_FLAGS] |= LRE_FLAG_NAMED_GROUPS;
+ }
+ dbuf_free(&s->group_names);
+
+#ifdef DUMP_REOP
+ lre_dump_bytecode(s->byte_code.buf, s->byte_code.size);
+#endif
+
+ error_msg[0] = '\0';
+ *plen = s->byte_code.size;
+ return s->byte_code.buf;
+}
+
+static BOOL is_line_terminator(uint32_t c)
+{
+ return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS);
+}
+
+static BOOL is_word_char(uint32_t c)
+{
+ return ((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c == '_'));
+}
+
+#define GET_CHAR(c, cptr, cbuf_end) \
+ do { \
+ if (cbuf_type == 0) { \
+ (c) = *(cptr)++; \
+ } else { \
+ uint32_t __c1; \
+ (c) = *(uint16_t *)(cptr); \
+ (cptr) += 2; \
+ if ((c) >= 0xd800 && (c) < 0xdc00 && \
+ cbuf_type == 2 && (cptr) < (cbuf_end)) { \
+ __c1 = *(uint16_t *)(cptr); \
+ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
+ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
+ (cptr) += 2; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define PEEK_CHAR(c, cptr, cbuf_end) \
+ do { \
+ if (cbuf_type == 0) { \
+ (c) = (cptr)[0]; \
+ } else { \
+ uint32_t __c1; \
+ (c) = ((uint16_t *)(cptr))[0]; \
+ if ((c) >= 0xd800 && (c) < 0xdc00 && \
+ cbuf_type == 2 && ((cptr) + 2) < (cbuf_end)) { \
+ __c1 = ((uint16_t *)(cptr))[1]; \
+ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
+ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define PEEK_PREV_CHAR(c, cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+ (c) = (cptr)[-1]; \
+ } else { \
+ uint32_t __c1; \
+ (c) = ((uint16_t *)(cptr))[-1]; \
+ if ((c) >= 0xdc00 && (c) < 0xe000 && \
+ cbuf_type == 2 && ((cptr) - 4) >= (cbuf_start)) { \
+ __c1 = ((uint16_t *)(cptr))[-2]; \
+ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
+ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define GET_PREV_CHAR(c, cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+ (cptr)--; \
+ (c) = (cptr)[0]; \
+ } else { \
+ uint32_t __c1; \
+ (cptr) -= 2; \
+ (c) = ((uint16_t *)(cptr))[0]; \
+ if ((c) >= 0xdc00 && (c) < 0xe000 && \
+ cbuf_type == 2 && (cptr) > (cbuf_start)) { \
+ __c1 = ((uint16_t *)(cptr))[-1]; \
+ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
+ (cptr) -= 2; \
+ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define PREV_CHAR(cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+ (cptr)--; \
+ } else { \
+ (cptr) -= 2; \
+ if (cbuf_type == 2) { \
+ c = ((uint16_t *)(cptr))[0]; \
+ if (c >= 0xdc00 && c < 0xe000 && (cptr) > (cbuf_start)) { \
+ c = ((uint16_t *)(cptr))[-1]; \
+ if (c >= 0xd800 && c < 0xdc00) \
+ (cptr) -= 2; \
+ } \
+ } \
+ } \
+ } while (0)
+
+typedef uintptr_t StackInt;
+
+typedef enum {
+ RE_EXEC_STATE_SPLIT,
+ RE_EXEC_STATE_LOOKAHEAD,
+ RE_EXEC_STATE_NEGATIVE_LOOKAHEAD,
+ RE_EXEC_STATE_GREEDY_QUANT,
+} REExecStateEnum;
+
+typedef struct REExecState {
+ REExecStateEnum type : 8;
+ uint8_t stack_len;
+ size_t count; /* only used for RE_EXEC_STATE_GREEDY_QUANT */
+ const uint8_t *cptr;
+ const uint8_t *pc;
+ void *buf[0];
+} REExecState;
+
+typedef struct {
+ const uint8_t *cbuf;
+ const uint8_t *cbuf_end;
+ /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */
+ int cbuf_type;
+ int capture_count;
+ int stack_size_max;
+ BOOL multi_line;
+ BOOL ignore_case;
+ BOOL is_utf16;
+ void *opaque; /* used for stack overflow check */
+
+ size_t state_size;
+ uint8_t *state_stack;
+ size_t state_stack_size;
+ size_t state_stack_len;
+} REExecContext;
+
+static int push_state(REExecContext *s,
+ uint8_t **capture,
+ const StackInt *stack, size_t stack_len,
+ const uint8_t *pc, const uint8_t *cptr,
+ REExecStateEnum type, size_t count)
+{
+ REExecState *rs;
+ uint8_t *new_stack;
+ size_t new_size, i, n;
+ StackInt *stack_buf;
+
+ if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) {
+ /* reallocate the stack */
+ new_size = s->state_stack_size * 3 / 2;
+ if (new_size < 8)
+ new_size = 8;
+ new_stack = lre_realloc(s->opaque, s->state_stack, new_size * s->state_size);
+ if (!new_stack)
+ return -1;
+ s->state_stack_size = new_size;
+ s->state_stack = new_stack;
+ }
+ rs = (REExecState *)(s->state_stack + s->state_stack_len * s->state_size);
+ s->state_stack_len++;
+ rs->type = type;
+ rs->count = count;
+ rs->stack_len = stack_len;
+ rs->cptr = cptr;
+ rs->pc = pc;
+ n = 2 * s->capture_count;
+ for(i = 0; i < n; i++)
+ rs->buf[i] = capture[i];
+ stack_buf = (StackInt *)(rs->buf + n);
+ for(i = 0; i < stack_len; i++)
+ stack_buf[i] = stack[i];
+ return 0;
+}
+
+/* return 1 if match, 0 if not match or -1 if error. */
+static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
+ StackInt *stack, int stack_len,
+ const uint8_t *pc, const uint8_t *cptr,
+ BOOL no_recurse)
+{
+ int opcode, ret;
+ int cbuf_type;
+ uint32_t val, c;
+ const uint8_t *cbuf_end;
+
+ cbuf_type = s->cbuf_type;
+ cbuf_end = s->cbuf_end;
+
+ for(;;) {
+ // printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN)));
+ opcode = *pc++;
+ switch(opcode) {
+ case REOP_match:
+ {
+ REExecState *rs;
+ if (no_recurse)
+ return (intptr_t)cptr;
+ ret = 1;
+ goto recurse;
+ no_match:
+ if (no_recurse)
+ return 0;
+ ret = 0;
+ recurse:
+ for(;;) {
+ if (s->state_stack_len == 0)
+ return ret;
+ rs = (REExecState *)(s->state_stack +
+ (s->state_stack_len - 1) * s->state_size);
+ if (rs->type == RE_EXEC_STATE_SPLIT) {
+ if (!ret) {
+ pop_state:
+ memcpy(capture, rs->buf,
+ sizeof(capture[0]) * 2 * s->capture_count);
+ pop_state1:
+ pc = rs->pc;
+ cptr = rs->cptr;
+ stack_len = rs->stack_len;
+ memcpy(stack, rs->buf + 2 * s->capture_count,
+ stack_len * sizeof(stack[0]));
+ s->state_stack_len--;
+ break;
+ }
+ } else if (rs->type == RE_EXEC_STATE_GREEDY_QUANT) {
+ if (!ret) {
+ uint32_t char_count, i;
+ memcpy(capture, rs->buf,
+ sizeof(capture[0]) * 2 * s->capture_count);
+ stack_len = rs->stack_len;
+ memcpy(stack, rs->buf + 2 * s->capture_count,
+ stack_len * sizeof(stack[0]));
+ pc = rs->pc;
+ cptr = rs->cptr;
+ /* go backward */
+ char_count = get_u32(pc + 12);
+ for(i = 0; i < char_count; i++) {
+ PREV_CHAR(cptr, s->cbuf);
+ }
+ pc = (pc + 16) + (int)get_u32(pc);
+ rs->cptr = cptr;
+ rs->count--;
+ if (rs->count == 0) {
+ s->state_stack_len--;
+ }
+ break;
+ }
+ } else {
+ ret = ((rs->type == RE_EXEC_STATE_LOOKAHEAD && ret) ||
+ (rs->type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD && !ret));
+ if (ret) {
+ /* keep the capture in case of positive lookahead */
+ if (rs->type == RE_EXEC_STATE_LOOKAHEAD)
+ goto pop_state1;
+ else
+ goto pop_state;
+ }
+ }
+ s->state_stack_len--;
+ }
+ }
+ break;
+ case REOP_char32:
+ val = get_u32(pc);
+ pc += 4;
+ goto test_char;
+ case REOP_char:
+ val = get_u16(pc);
+ pc += 2;
+ test_char:
+ if (cptr >= cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ if (s->ignore_case) {
+ c = lre_canonicalize(c, s->is_utf16);
+ }
+ if (val != c)
+ goto no_match;
+ break;
+ case REOP_split_goto_first:
+ case REOP_split_next_first:
+ {
+ const uint8_t *pc1;
+
+ val = get_u32(pc);
+ pc += 4;
+ if (opcode == REOP_split_next_first) {
+ pc1 = pc + (int)val;
+ } else {
+ pc1 = pc;
+ pc = pc + (int)val;
+ }
+ ret = push_state(s, capture, stack, stack_len,
+ pc1, cptr, RE_EXEC_STATE_SPLIT, 0);
+ if (ret < 0)
+ return -1;
+ break;
+ }
+ case REOP_lookahead:
+ case REOP_negative_lookahead:
+ val = get_u32(pc);
+ pc += 4;
+ ret = push_state(s, capture, stack, stack_len,
+ pc + (int)val, cptr,
+ RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead,
+ 0);
+ if (ret < 0)
+ return -1;
+ break;
+
+ case REOP_goto:
+ val = get_u32(pc);
+ pc += 4 + (int)val;
+ break;
+ case REOP_line_start:
+ if (cptr == s->cbuf)
+ break;
+ if (!s->multi_line)
+ goto no_match;
+ PEEK_PREV_CHAR(c, cptr, s->cbuf);
+ if (!is_line_terminator(c))
+ goto no_match;
+ break;
+ case REOP_line_end:
+ if (cptr == cbuf_end)
+ break;
+ if (!s->multi_line)
+ goto no_match;
+ PEEK_CHAR(c, cptr, cbuf_end);
+ if (!is_line_terminator(c))
+ goto no_match;
+ break;
+ case REOP_dot:
+ if (cptr == cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ if (is_line_terminator(c))
+ goto no_match;
+ break;
+ case REOP_any:
+ if (cptr == cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ break;
+ case REOP_save_start:
+ case REOP_save_end:
+ val = *pc++;
+ assert(val < s->capture_count);
+ capture[2 * val + opcode - REOP_save_start] = (uint8_t *)cptr;
+ break;
+ case REOP_save_reset:
+ {
+ uint32_t val2;
+ val = pc[0];
+ val2 = pc[1];
+ pc += 2;
+ assert(val2 < s->capture_count);
+ while (val <= val2) {
+ capture[2 * val] = NULL;
+ capture[2 * val + 1] = NULL;
+ val++;
+ }
+ }
+ break;
+ case REOP_push_i32:
+ val = get_u32(pc);
+ pc += 4;
+ stack[stack_len++] = val;
+ break;
+ case REOP_drop:
+ stack_len--;
+ break;
+ case REOP_loop:
+ val = get_u32(pc);
+ pc += 4;
+ if (--stack[stack_len - 1] != 0) {
+ pc += (int)val;
+ }
+ break;
+ case REOP_push_char_pos:
+ stack[stack_len++] = (uintptr_t)cptr;
+ break;
+ case REOP_bne_char_pos:
+ val = get_u32(pc);
+ pc += 4;
+ if (stack[--stack_len] != (uintptr_t)cptr)
+ pc += (int)val;
+ break;
+ case REOP_word_boundary:
+ case REOP_not_word_boundary:
+ {
+ BOOL v1, v2;
+ /* char before */
+ if (cptr == s->cbuf) {
+ v1 = FALSE;
+ } else {
+ PEEK_PREV_CHAR(c, cptr, s->cbuf);
+ v1 = is_word_char(c);
+ }
+ /* current char */
+ if (cptr >= cbuf_end) {
+ v2 = FALSE;
+ } else {
+ PEEK_CHAR(c, cptr, cbuf_end);
+ v2 = is_word_char(c);
+ }
+ if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode))
+ goto no_match;
+ }
+ break;
+ case REOP_back_reference:
+ case REOP_backward_back_reference:
+ {
+ const uint8_t *cptr1, *cptr1_end, *cptr1_start;
+ uint32_t c1, c2;
+
+ val = *pc++;
+ if (val >= s->capture_count)
+ goto no_match;
+ cptr1_start = capture[2 * val];
+ cptr1_end = capture[2 * val + 1];
+ if (!cptr1_start || !cptr1_end)
+ break;
+ if (opcode == REOP_back_reference) {
+ cptr1 = cptr1_start;
+ while (cptr1 < cptr1_end) {
+ if (cptr >= cbuf_end)
+ goto no_match;
+ GET_CHAR(c1, cptr1, cptr1_end);
+ GET_CHAR(c2, cptr, cbuf_end);
+ if (s->ignore_case) {
+ c1 = lre_canonicalize(c1, s->is_utf16);
+ c2 = lre_canonicalize(c2, s->is_utf16);
+ }
+ if (c1 != c2)
+ goto no_match;
+ }
+ } else {
+ cptr1 = cptr1_end;
+ while (cptr1 > cptr1_start) {
+ if (cptr == s->cbuf)
+ goto no_match;
+ GET_PREV_CHAR(c1, cptr1, cptr1_start);
+ GET_PREV_CHAR(c2, cptr, s->cbuf);
+ if (s->ignore_case) {
+ c1 = lre_canonicalize(c1, s->is_utf16);
+ c2 = lre_canonicalize(c2, s->is_utf16);
+ }
+ if (c1 != c2)
+ goto no_match;
+ }
+ }
+ }
+ break;
+ case REOP_range:
+ {
+ int n;
+ uint32_t low, high, idx_min, idx_max, idx;
+
+ n = get_u16(pc); /* n must be >= 1 */
+ pc += 2;
+ if (cptr >= cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ if (s->ignore_case) {
+ c = lre_canonicalize(c, s->is_utf16);
+ }
+ idx_min = 0;
+ low = get_u16(pc + 0 * 4);
+ if (c < low)
+ goto no_match;
+ idx_max = n - 1;
+ high = get_u16(pc + idx_max * 4 + 2);
+ /* 0xffff in for last value means +infinity */
+ if (unlikely(c >= 0xffff) && high == 0xffff)
+ goto range_match;
+ if (c > high)
+ goto no_match;
+ while (idx_min <= idx_max) {
+ idx = (idx_min + idx_max) / 2;
+ low = get_u16(pc + idx * 4);
+ high = get_u16(pc + idx * 4 + 2);
+ if (c < low)
+ idx_max = idx - 1;
+ else if (c > high)
+ idx_min = idx + 1;
+ else
+ goto range_match;
+ }
+ goto no_match;
+ range_match:
+ pc += 4 * n;
+ }
+ break;
+ case REOP_range32:
+ {
+ int n;
+ uint32_t low, high, idx_min, idx_max, idx;
+
+ n = get_u16(pc); /* n must be >= 1 */
+ pc += 2;
+ if (cptr >= cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ if (s->ignore_case) {
+ c = lre_canonicalize(c, s->is_utf16);
+ }
+ idx_min = 0;
+ low = get_u32(pc + 0 * 8);
+ if (c < low)
+ goto no_match;
+ idx_max = n - 1;
+ high = get_u32(pc + idx_max * 8 + 4);
+ if (c > high)
+ goto no_match;
+ while (idx_min <= idx_max) {
+ idx = (idx_min + idx_max) / 2;
+ low = get_u32(pc + idx * 8);
+ high = get_u32(pc + idx * 8 + 4);
+ if (c < low)
+ idx_max = idx - 1;
+ else if (c > high)
+ idx_min = idx + 1;
+ else
+ goto range32_match;
+ }
+ goto no_match;
+ range32_match:
+ pc += 8 * n;
+ }
+ break;
+ case REOP_prev:
+ /* go to the previous char */
+ if (cptr == s->cbuf)
+ goto no_match;
+ PREV_CHAR(cptr, s->cbuf);
+ break;
+ case REOP_simple_greedy_quant:
+ {
+ uint32_t next_pos, quant_min, quant_max;
+ size_t q;
+ intptr_t res;
+ const uint8_t *pc1;
+
+ next_pos = get_u32(pc);
+ quant_min = get_u32(pc + 4);
+ quant_max = get_u32(pc + 8);
+ pc += 16;
+ pc1 = pc;
+ pc += (int)next_pos;
+
+ q = 0;
+ for(;;) {
+ res = lre_exec_backtrack(s, capture, stack, stack_len,
+ pc1, cptr, TRUE);
+ if (res == -1)
+ return res;
+ if (!res)
+ break;
+ cptr = (uint8_t *)res;
+ q++;
+ if (q >= quant_max && quant_max != INT32_MAX)
+ break;
+ }
+ if (q < quant_min)
+ goto no_match;
+ if (q > quant_min) {
+ /* will examine all matches down to quant_min */
+ ret = push_state(s, capture, stack, stack_len,
+ pc1 - 16, cptr,
+ RE_EXEC_STATE_GREEDY_QUANT,
+ q - quant_min);
+ if (ret < 0)
+ return -1;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+}
+
+/* Return 1 if match, 0 if not match or -1 if error. cindex is the
+ starting position of the match and must be such as 0 <= cindex <=
+ clen. */
+int lre_exec(uint8_t **capture,
+ const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
+ int cbuf_type, void *opaque)
+{
+ REExecContext s_s, *s = &s_s;
+ int re_flags, i, alloca_size, ret;
+ StackInt *stack_buf;
+
+ re_flags = bc_buf[RE_HEADER_FLAGS];
+ s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0;
+ s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0;
+ s->is_utf16 = (re_flags & LRE_FLAG_UTF16) != 0;
+ s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT];
+ s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE];
+ s->cbuf = cbuf;
+ s->cbuf_end = cbuf + (clen << cbuf_type);
+ s->cbuf_type = cbuf_type;
+ if (s->cbuf_type == 1 && s->is_utf16)
+ s->cbuf_type = 2;
+ s->opaque = opaque;
+
+ s->state_size = sizeof(REExecState) +
+ s->capture_count * sizeof(capture[0]) * 2 +
+ s->stack_size_max * sizeof(stack_buf[0]);
+ s->state_stack = NULL;
+ s->state_stack_len = 0;
+ s->state_stack_size = 0;
+
+ for(i = 0; i < s->capture_count * 2; i++)
+ capture[i] = NULL;
+ alloca_size = s->stack_size_max * sizeof(stack_buf[0]);
+ stack_buf = alloca(alloca_size);
+ ret = lre_exec_backtrack(s, capture, stack_buf, 0, bc_buf + RE_HEADER_LEN,
+ cbuf + (cindex << cbuf_type), FALSE);
+ lre_realloc(s->opaque, s->state_stack, 0);
+ return ret;
+}
+
+int lre_get_capture_count(const uint8_t *bc_buf)
+{
+ return bc_buf[RE_HEADER_CAPTURE_COUNT];
+}
+
+int lre_get_flags(const uint8_t *bc_buf)
+{
+ return bc_buf[RE_HEADER_FLAGS];
+}
+
+/* Return NULL if no group names. Otherwise, return a pointer to
+ 'capture_count - 1' zero terminated UTF-8 strings. */
+const char *lre_get_groupnames(const uint8_t *bc_buf)
+{
+ uint32_t re_bytecode_len;
+ if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0)
+ return NULL;
+ re_bytecode_len = get_u32(bc_buf + 3);
+ return (const char *)(bc_buf + 7 + re_bytecode_len);
+}
+
+#ifdef TEST
+
+BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
+{
+ return FALSE;
+}
+
+void *lre_realloc(void *opaque, void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+int main(int argc, char **argv)
+{
+ int len, ret, i;
+ uint8_t *bc;
+ char error_msg[64];
+ uint8_t *capture[CAPTURE_COUNT_MAX * 2];
+ const char *input;
+ int input_len, capture_count;
+
+ if (argc < 3) {
+ printf("usage: %s regexp input\n", argv[0]);
+ exit(1);
+ }
+ bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1],
+ strlen(argv[1]), 0, NULL);
+ if (!bc) {
+ fprintf(stderr, "error: %s\n", error_msg);
+ exit(1);
+ }
+
+ input = argv[2];
+ input_len = strlen(input);
+
+ ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL);
+ printf("ret=%d\n", ret);
+ if (ret == 1) {
+ capture_count = lre_get_capture_count(bc);
+ for(i = 0; i < 2 * capture_count; i++) {
+ uint8_t *ptr;
+ ptr = capture[i];
+ printf("%d: ", i);
+ if (!ptr)
+ printf("<nil>");
+ else
+ printf("%u", (int)(ptr - (uint8_t *)input));
+ printf("\n");
+ }
+ }
+ return 0;
+}
+#endif
diff --git a/src/shared/quickjs/libregexp.h b/src/shared/quickjs/libregexp.h
new file mode 100644
index 000000000..9aedb7e93
--- /dev/null
+++ b/src/shared/quickjs/libregexp.h
@@ -0,0 +1,92 @@
+/*
+ * 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);
+const char *lre_get_groupnames(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 */
diff --git a/src/shared/quickjs/libunicode-table.h b/src/shared/quickjs/libunicode-table.h
new file mode 100644
index 000000000..1727525fb
--- /dev/null
+++ b/src/shared/quickjs/libunicode-table.h
@@ -0,0 +1,4449 @@
+/* Compressed unicode tables */
+/* Automatically generated file - do not edit */
+
+#include <stdint.h>
+
+static const uint32_t case_conv_table1[370] = {
+ 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730,
+ 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700,
+ 0x007f8100, 0x00803040, 0x009801c3, 0x00988190,
+ 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40,
+ 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100,
+ 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240,
+ 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130,
+ 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130,
+ 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240,
+ 0x00cd0100, 0x00ce0130, 0x00ce8130, 0x00cf0100,
+ 0x00cf8130, 0x00d00640, 0x00d30130, 0x00d38240,
+ 0x00d48130, 0x00d60240, 0x00d70130, 0x00d78240,
+ 0x00d88230, 0x00d98440, 0x00db8130, 0x00dc0240,
+ 0x00de0240, 0x00df8100, 0x00e20350, 0x00e38350,
+ 0x00e50350, 0x00e69040, 0x00ee8100, 0x00ef1240,
+ 0x00f801b4, 0x00f88350, 0x00fa0240, 0x00fb0130,
+ 0x00fb8130, 0x00fc2840, 0x01100130, 0x01111240,
+ 0x011d0131, 0x011d8240, 0x011e8130, 0x011f0131,
+ 0x011f8201, 0x01208240, 0x01218130, 0x01220130,
+ 0x01228130, 0x01230a40, 0x01280101, 0x01288101,
+ 0x01290101, 0x01298100, 0x012a0100, 0x012b0200,
+ 0x012c8100, 0x012d8100, 0x012e0101, 0x01300100,
+ 0x01308101, 0x01318100, 0x01328101, 0x01330101,
+ 0x01340100, 0x01348100, 0x01350101, 0x01358101,
+ 0x01360101, 0x01378100, 0x01388101, 0x01390100,
+ 0x013a8100, 0x013e8101, 0x01400100, 0x01410101,
+ 0x01418100, 0x01438101, 0x01440100, 0x01448100,
+ 0x01450200, 0x01460100, 0x01490100, 0x014e8101,
+ 0x014f0101, 0x01a28173, 0x01b80440, 0x01bb0240,
+ 0x01bd8300, 0x01bf8130, 0x01c30130, 0x01c40330,
+ 0x01c60130, 0x01c70230, 0x01c801d0, 0x01c89130,
+ 0x01d18930, 0x01d60100, 0x01d68300, 0x01d801d3,
+ 0x01d89100, 0x01e10173, 0x01e18900, 0x01e60100,
+ 0x01e68200, 0x01e78130, 0x01e80173, 0x01e88173,
+ 0x01ea8173, 0x01eb0173, 0x01eb8100, 0x01ec1840,
+ 0x01f80173, 0x01f88173, 0x01f90100, 0x01f98100,
+ 0x01fa01a0, 0x01fa8173, 0x01fb8240, 0x01fc8130,
+ 0x01fd0240, 0x01fe8330, 0x02001030, 0x02082030,
+ 0x02182000, 0x02281000, 0x02302240, 0x02453640,
+ 0x02600130, 0x02608e40, 0x02678100, 0x02686040,
+ 0x0298a630, 0x02b0a600, 0x02c381b5, 0x08502631,
+ 0x08638131, 0x08668131, 0x08682b00, 0x087e8300,
+ 0x09d05011, 0x09f80610, 0x09fc0620, 0x0e400174,
+ 0x0e408174, 0x0e410174, 0x0e418174, 0x0e420174,
+ 0x0e428174, 0x0e430174, 0x0e438180, 0x0e440180,
+ 0x0e482b30, 0x0e5e8330, 0x0ebc8101, 0x0ebe8101,
+ 0x0ec70101, 0x0f007e40, 0x0f3f1840, 0x0f4b01b5,
+ 0x0f4b81b6, 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7,
+ 0x0f4d8180, 0x0f4f0130, 0x0f506040, 0x0f800800,
+ 0x0f840830, 0x0f880600, 0x0f8c0630, 0x0f900800,
+ 0x0f940830, 0x0f980800, 0x0f9c0830, 0x0fa00600,
+ 0x0fa40630, 0x0fa801b0, 0x0fa88100, 0x0fa901d3,
+ 0x0fa98100, 0x0faa01d3, 0x0faa8100, 0x0fab01d3,
+ 0x0fab8100, 0x0fac8130, 0x0fad8130, 0x0fae8130,
+ 0x0faf8130, 0x0fb00800, 0x0fb40830, 0x0fb80200,
+ 0x0fb90400, 0x0fbb0200, 0x0fbc0201, 0x0fbd0201,
+ 0x0fbe0201, 0x0fc008b7, 0x0fc40867, 0x0fc808b8,
+ 0x0fcc0868, 0x0fd008b8, 0x0fd40868, 0x0fd80200,
+ 0x0fd901b9, 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1,
+ 0x0fdb81d7, 0x0fdc0230, 0x0fdd0230, 0x0fde0161,
+ 0x0fdf0173, 0x0fe101b9, 0x0fe181b2, 0x0fe201ba,
+ 0x0fe301b2, 0x0fe381d8, 0x0fe40430, 0x0fe60162,
+ 0x0fe80200, 0x0fe901d0, 0x0fe981d0, 0x0feb01b0,
+ 0x0feb81d0, 0x0fec0230, 0x0fed0230, 0x0ff00201,
+ 0x0ff101d3, 0x0ff181d3, 0x0ff201ba, 0x0ff28101,
+ 0x0ff301b0, 0x0ff381d3, 0x0ff40230, 0x0ff50230,
+ 0x0ff60131, 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb,
+ 0x0ffb01b2, 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230,
+ 0x0ffe0162, 0x109301a0, 0x109501a0, 0x109581a0,
+ 0x10990131, 0x10a70101, 0x10b01031, 0x10b81001,
+ 0x10c18240, 0x125b1a31, 0x12681a01, 0x16003031,
+ 0x16183001, 0x16300240, 0x16310130, 0x16318130,
+ 0x16320130, 0x16328100, 0x16330100, 0x16338640,
+ 0x16368130, 0x16370130, 0x16378130, 0x16380130,
+ 0x16390240, 0x163a8240, 0x163f0230, 0x16406440,
+ 0x16758440, 0x16790240, 0x16802600, 0x16938100,
+ 0x16968100, 0x53202e40, 0x53401c40, 0x53910e40,
+ 0x53993e40, 0x53bc8440, 0x53be8130, 0x53bf0a40,
+ 0x53c58240, 0x53c68130, 0x53c80440, 0x53ca0101,
+ 0x53cb1440, 0x53d50130, 0x53d58130, 0x53d60130,
+ 0x53d68130, 0x53d70130, 0x53d80130, 0x53d88130,
+ 0x53d90130, 0x53d98131, 0x53da1040, 0x53e20131,
+ 0x53e28130, 0x53e30130, 0x53e38440, 0x53e80240,
+ 0x53eb0440, 0x53fa8240, 0x55a98101, 0x55b85020,
+ 0x7d8001b2, 0x7d8081b2, 0x7d8101b2, 0x7d8181da,
+ 0x7d8201da, 0x7d8281b3, 0x7d8301b3, 0x7d8981bb,
+ 0x7d8a01bb, 0x7d8a81bb, 0x7d8b01bc, 0x7d8b81bb,
+ 0x7f909a31, 0x7fa09a01, 0x82002831, 0x82142801,
+ 0x82582431, 0x826c2401, 0x82b80b31, 0x82be0f31,
+ 0x82c60731, 0x82ca0231, 0x82cb8b01, 0x82d18f01,
+ 0x82d98701, 0x82dd8201, 0x86403331, 0x86603301,
+ 0x8c502031, 0x8c602001, 0xb7202031, 0xb7302001,
+ 0xf4802231, 0xf4912201,
+};
+
+static const uint8_t case_conv_table2[370] = {
+ 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04,
+ 0x10, 0x00, 0x8f, 0x0b, 0x00, 0x00, 0x11, 0x00,
+ 0x08, 0x00, 0x53, 0x4a, 0x51, 0x00, 0x52, 0x00,
+ 0x53, 0x00, 0x3a, 0x54, 0x55, 0x00, 0x57, 0x59,
+ 0x3f, 0x5d, 0x5c, 0x00, 0x46, 0x61, 0x63, 0x42,
+ 0x64, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00,
+ 0x6c, 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, 0x20,
+ 0x35, 0x00, 0x27, 0x00, 0x21, 0x00, 0x24, 0x22,
+ 0x2a, 0x00, 0x13, 0x6b, 0x6d, 0x00, 0x26, 0x24,
+ 0x27, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x3e, 0x1e,
+ 0x3f, 0x1f, 0x39, 0x3d, 0x22, 0x21, 0x41, 0x1e,
+ 0x40, 0x25, 0x25, 0x26, 0x28, 0x20, 0x2a, 0x48,
+ 0x2c, 0x43, 0x2e, 0x4b, 0x30, 0x4c, 0x32, 0x44,
+ 0x42, 0x99, 0x00, 0x00, 0x95, 0x8f, 0x7d, 0x7e,
+ 0x83, 0x84, 0x12, 0x80, 0x82, 0x76, 0x77, 0x12,
+ 0x7b, 0xa3, 0x7c, 0x78, 0x79, 0x8a, 0x92, 0x98,
+ 0xa6, 0xa0, 0x85, 0x00, 0x9a, 0xa1, 0x93, 0x75,
+ 0x33, 0x95, 0x00, 0x8e, 0x00, 0x74, 0x99, 0x98,
+ 0x97, 0x96, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00,
+ 0xa1, 0xa0, 0x15, 0x2e, 0x2f, 0x30, 0xb4, 0xb5,
+ 0x4f, 0xaa, 0xa9, 0x12, 0x14, 0x1e, 0x21, 0x22,
+ 0x22, 0x2a, 0x34, 0x35, 0xa6, 0xa7, 0x36, 0x1f,
+ 0x49, 0x00, 0x00, 0x97, 0x01, 0x5a, 0xda, 0x1d,
+ 0x36, 0x05, 0x00, 0xc4, 0xc3, 0xc6, 0xc5, 0xc8,
+ 0xc7, 0xca, 0xc9, 0xcc, 0xcb, 0xc4, 0xd5, 0x45,
+ 0xd6, 0x42, 0xd7, 0x46, 0xd8, 0xce, 0xd0, 0xd2,
+ 0xd4, 0xda, 0xd9, 0xee, 0xf6, 0xfe, 0x0e, 0x07,
+ 0x0f, 0x80, 0x9f, 0x00, 0x21, 0x80, 0xa3, 0xed,
+ 0x00, 0xc0, 0x40, 0xc6, 0x60, 0xe7, 0xdb, 0xe6,
+ 0x99, 0xc0, 0x00, 0x00, 0x06, 0x60, 0xdc, 0x29,
+ 0xfd, 0x15, 0x12, 0x06, 0x16, 0xf8, 0xdd, 0x06,
+ 0x15, 0x12, 0x84, 0x08, 0xc6, 0x16, 0xff, 0xdf,
+ 0x03, 0xc0, 0x40, 0x00, 0x46, 0x60, 0xde, 0xe0,
+ 0x6d, 0x37, 0x38, 0x39, 0x15, 0x14, 0x17, 0x16,
+ 0x00, 0x1a, 0x19, 0x1c, 0x1b, 0x00, 0x5f, 0xb7,
+ 0x65, 0x44, 0x47, 0x00, 0x4f, 0x62, 0x4e, 0x50,
+ 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xa3, 0xa4,
+ 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00,
+ 0x00, 0x5a, 0x00, 0x47, 0x00, 0x5b, 0x56, 0x58,
+ 0x60, 0x5e, 0x70, 0x69, 0x6f, 0x4e, 0x00, 0x3b,
+ 0x67, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x45, 0xa8,
+ 0x8a, 0x8b, 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf,
+ 0x94, 0xb0, 0x6f, 0xb2, 0x5d, 0x5c, 0x5f, 0x5e,
+ 0x61, 0x60, 0x66, 0x67, 0x68, 0x69, 0x62, 0x63,
+ 0x64, 0x65, 0x6b, 0x6a, 0x6d, 0x6c, 0x6f, 0x6e,
+ 0x71, 0x70,
+};
+
+static const uint16_t case_conv_ext[58] = {
+ 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391,
+ 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307,
+ 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331,
+ 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80,
+ 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca,
+ 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546,
+ 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9,
+ 0x006b, 0x00e5,
+};
+
+static const uint8_t unicode_prop_Cased1_table[188] = {
+ 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3,
+ 0x80, 0x8c, 0x80, 0x8d, 0x81, 0x8d, 0x02, 0x80,
+ 0xe1, 0x80, 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01,
+ 0x11, 0x00, 0x01, 0x04, 0x08, 0x01, 0x08, 0x30,
+ 0x08, 0x01, 0x15, 0x20, 0x00, 0x39, 0x99, 0x31,
+ 0x9d, 0x84, 0x40, 0x94, 0x80, 0xd6, 0x82, 0xa6,
+ 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x57, 0x76,
+ 0xf8, 0x02, 0x80, 0x8f, 0x80, 0xb0, 0x40, 0xdb,
+ 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f,
+ 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28,
+ 0x10, 0x11, 0x02, 0x01, 0x18, 0x0b, 0x24, 0x4b,
+ 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79,
+ 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94,
+ 0x05, 0x80, 0x98, 0x80, 0xa2, 0x00, 0x80, 0xa1,
+ 0x82, 0x43, 0x34, 0xa2, 0x06, 0x80, 0x8c, 0x60,
+ 0x5c, 0x16, 0x01, 0x10, 0xa9, 0x80, 0x88, 0x60,
+ 0xcc, 0x44, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09,
+ 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03,
+ 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16,
+ 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80,
+ 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80,
+ 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07,
+ 0x47, 0x33, 0x89, 0x80, 0x93, 0x52, 0x10, 0x99,
+ 0x85, 0x99, 0x85, 0x99,
+};
+
+static const uint8_t unicode_prop_Cased1_index[18] = {
+ 0xb9, 0x02, 0xe0, 0xa0, 0x1e, 0x40, 0x9e, 0xa6,
+ 0x40, 0x55, 0xd4, 0x61, 0xfb, 0xd6, 0x21, 0x8a,
+ 0xf1, 0x01,
+};
+
+static const uint8_t unicode_prop_Case_Ignorable_table[720] = {
+ 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80,
+ 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6,
+ 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40,
+ 0xfa, 0x86, 0x40, 0xce, 0x04, 0x80, 0xb0, 0xac,
+ 0x00, 0x01, 0x01, 0x00, 0xab, 0x80, 0x8a, 0x85,
+ 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f,
+ 0x80, 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80,
+ 0x9d, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0x18, 0x08,
+ 0x97, 0x97, 0xaa, 0x82, 0xab, 0x06, 0x0d, 0x87,
+ 0xa8, 0xb9, 0xb6, 0x00, 0x03, 0x3b, 0x02, 0x86,
+ 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03,
+ 0x1f, 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8,
+ 0x03, 0x0b, 0x09, 0x12, 0x80, 0x9d, 0x0a, 0x80,
+ 0x8a, 0x81, 0xb8, 0x03, 0x20, 0x0b, 0x80, 0x93,
+ 0x81, 0x95, 0x28, 0x80, 0xb9, 0x01, 0x00, 0x1f,
+ 0x06, 0x81, 0x8a, 0x81, 0x9d, 0x80, 0xbc, 0x80,
+ 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb6, 0x00, 0x14,
+ 0x10, 0x1e, 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9,
+ 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, 0x9b, 0x81,
+ 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0x9c, 0x80,
+ 0xc7, 0x06, 0x10, 0x80, 0xd9, 0x01, 0x86, 0x8a,
+ 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x85, 0xc9,
+ 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04,
+ 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe5,
+ 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f,
+ 0x83, 0x8c, 0x01, 0x0d, 0x80, 0x8e, 0x80, 0xdd,
+ 0x80, 0x42, 0x5f, 0x82, 0x43, 0xb1, 0x82, 0x9c,
+ 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37,
+ 0x01, 0x8a, 0x10, 0x20, 0xac, 0x84, 0xb2, 0x80,
+ 0xc0, 0x81, 0xa1, 0x80, 0xf5, 0x13, 0x81, 0x88,
+ 0x05, 0x82, 0x40, 0xda, 0x09, 0x80, 0xb9, 0x00,
+ 0x30, 0x00, 0x01, 0x3d, 0x89, 0x08, 0xa6, 0x07,
+ 0x9e, 0xb0, 0x83, 0xaf, 0x00, 0x20, 0x04, 0x80,
+ 0xa7, 0x88, 0x8b, 0x81, 0x9f, 0x19, 0x08, 0x82,
+ 0xb7, 0x00, 0x0a, 0x00, 0x82, 0xb9, 0x39, 0x81,
+ 0xbf, 0x85, 0xd1, 0x10, 0x8c, 0x06, 0x18, 0x28,
+ 0x11, 0xb1, 0xbe, 0x8c, 0x80, 0xa1, 0xe4, 0x41,
+ 0xbc, 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c,
+ 0x82, 0x8c, 0x81, 0x8b, 0x27, 0x81, 0x89, 0x01,
+ 0x01, 0x84, 0xb0, 0x20, 0x89, 0x00, 0x8c, 0x80,
+ 0x8f, 0x8c, 0xb2, 0xa0, 0x4b, 0x8a, 0x81, 0xf0,
+ 0x82, 0xfc, 0x80, 0x8e, 0x80, 0xdf, 0x9f, 0xae,
+ 0x80, 0x41, 0xd4, 0x80, 0xa3, 0x1a, 0x24, 0x80,
+ 0xdc, 0x85, 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80,
+ 0x44, 0xe1, 0x85, 0x41, 0x0d, 0x80, 0xe1, 0x18,
+ 0x89, 0x00, 0x9b, 0x83, 0xcf, 0x81, 0x8d, 0xa1,
+ 0xcd, 0x80, 0x96, 0x82, 0xe6, 0x12, 0x0f, 0x02,
+ 0x03, 0x80, 0x98, 0x0c, 0x80, 0x40, 0x96, 0x81,
+ 0x99, 0x91, 0x8c, 0x80, 0xa5, 0x87, 0x98, 0x8a,
+ 0xad, 0x82, 0xaf, 0x01, 0x19, 0x81, 0x90, 0x80,
+ 0x94, 0x81, 0xc1, 0x29, 0x09, 0x81, 0x8b, 0x07,
+ 0x80, 0xa2, 0x80, 0x8a, 0x80, 0xb2, 0x00, 0x11,
+ 0x0c, 0x08, 0x80, 0x9a, 0x80, 0x8d, 0x0c, 0x08,
+ 0x80, 0xe3, 0x84, 0x88, 0x82, 0xf8, 0x01, 0x03,
+ 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x90,
+ 0x42, 0x3c, 0x8f, 0x10, 0x8b, 0x8f, 0xa1, 0x01,
+ 0x80, 0x40, 0xa8, 0x06, 0x05, 0x80, 0x8a, 0x80,
+ 0xa2, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2,
+ 0x80, 0x94, 0x82, 0x42, 0x00, 0x80, 0x40, 0xe1,
+ 0x80, 0x40, 0x94, 0x84, 0x44, 0x04, 0x28, 0xa9,
+ 0x80, 0x88, 0x42, 0x45, 0x10, 0x0c, 0x83, 0xa7,
+ 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83,
+ 0x41, 0x82, 0x81, 0x40, 0x98, 0x8a, 0xb0, 0x83,
+ 0xfa, 0x80, 0xb5, 0x8e, 0xa8, 0x01, 0x81, 0x89,
+ 0x82, 0xb0, 0x19, 0x09, 0x03, 0x80, 0x89, 0x80,
+ 0xb1, 0x82, 0xa3, 0x20, 0x87, 0xbd, 0x80, 0x8b,
+ 0x81, 0xb3, 0x88, 0x89, 0x19, 0x80, 0xde, 0x11,
+ 0x00, 0x0d, 0x80, 0x40, 0x9f, 0x02, 0x87, 0x94,
+ 0x81, 0xb8, 0x0a, 0x80, 0xa4, 0x32, 0x84, 0x40,
+ 0xc2, 0x39, 0x10, 0x80, 0x96, 0x80, 0xd3, 0x28,
+ 0x03, 0x08, 0x81, 0x40, 0xed, 0x1d, 0x08, 0x81,
+ 0x9a, 0x81, 0xd4, 0x39, 0x00, 0x81, 0xe9, 0x00,
+ 0x01, 0x28, 0x80, 0xe4, 0x11, 0x18, 0x84, 0x41,
+ 0x02, 0x88, 0x01, 0x40, 0xff, 0x08, 0x03, 0x80,
+ 0x40, 0x8f, 0x19, 0x0b, 0x80, 0x9f, 0x89, 0xa7,
+ 0x29, 0x1f, 0x80, 0x88, 0x29, 0x82, 0xad, 0x8c,
+ 0x01, 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95,
+ 0x0e, 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30,
+ 0x80, 0xc7, 0x0a, 0x00, 0x80, 0x41, 0x5a, 0x81,
+ 0x55, 0x3a, 0x88, 0x60, 0x36, 0xb6, 0x84, 0xba,
+ 0x86, 0x88, 0x83, 0x44, 0x0a, 0x80, 0xbe, 0x90,
+ 0xbf, 0x08, 0x81, 0x60, 0x40, 0x0a, 0x18, 0x30,
+ 0x81, 0x4c, 0x9d, 0x08, 0x83, 0x52, 0x5b, 0xad,
+ 0x81, 0x96, 0x42, 0x1f, 0x82, 0x88, 0x8f, 0x0e,
+ 0x9d, 0x83, 0x40, 0x93, 0x82, 0x47, 0xba, 0xb6,
+ 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e,
+ 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0x41,
+ 0x04, 0x8d, 0x41, 0x6f, 0x80, 0xbc, 0x83, 0x45,
+ 0xdf, 0x86, 0xec, 0x87, 0x4a, 0xae, 0x84, 0x6c,
+ 0x0c, 0x00, 0x80, 0x9d, 0xdf, 0xff, 0x40, 0xef,
+};
+
+static const uint8_t unicode_prop_Case_Ignorable_index[69] = {
+ 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a,
+ 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f,
+ 0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20,
+ 0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0,
+ 0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56,
+ 0xfe, 0x20, 0xb1, 0x07, 0x01, 0x82, 0x10, 0x21,
+ 0x02, 0x13, 0x21, 0xb8, 0x16, 0x61, 0x97, 0x1a,
+ 0x01, 0x37, 0x6b, 0x21, 0x8c, 0xd1, 0x01, 0xd7,
+ 0xe8, 0x41, 0xf0, 0x01, 0x0e,
+};
+
+static const uint8_t unicode_prop_ID_Start_table[1079] = {
+ 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03,
+ 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83,
+ 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20,
+ 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80,
+ 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80,
+ 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac,
+ 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81,
+ 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8,
+ 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95,
+ 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a,
+ 0x84, 0x97, 0x05, 0x90, 0xa9, 0xb9, 0xb5, 0x10,
+ 0x91, 0x06, 0x89, 0x8e, 0x8f, 0x1f, 0x09, 0x81,
+ 0x95, 0x06, 0x00, 0x13, 0x10, 0x8f, 0x80, 0x8c,
+ 0x08, 0x82, 0x8d, 0x81, 0x89, 0x07, 0x2b, 0x09,
+ 0x95, 0x06, 0x01, 0x01, 0x01, 0x9e, 0x18, 0x80,
+ 0x92, 0x82, 0x8f, 0x88, 0x02, 0x80, 0x95, 0x06,
+ 0x01, 0x04, 0x10, 0x91, 0x80, 0x8e, 0x81, 0x96,
+ 0x80, 0x8a, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04,
+ 0x10, 0x9d, 0x08, 0x82, 0x8e, 0x80, 0x90, 0x00,
+ 0x2a, 0x10, 0x1a, 0x08, 0x00, 0x0a, 0x0a, 0x12,
+ 0x8b, 0x95, 0x80, 0xb3, 0x38, 0x10, 0x96, 0x80,
+ 0x8f, 0x10, 0x99, 0x11, 0x01, 0x81, 0x9d, 0x03,
+ 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9e,
+ 0x08, 0x81, 0x8e, 0x81, 0x90, 0x88, 0x02, 0x80,
+ 0xa8, 0x08, 0x8f, 0x04, 0x17, 0x82, 0x97, 0x2c,
+ 0x91, 0x82, 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9,
+ 0xaf, 0x01, 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20,
+ 0x97, 0x00, 0x80, 0x89, 0x01, 0x88, 0x01, 0x20,
+ 0x80, 0x94, 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3,
+ 0x9a, 0x84, 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b,
+ 0x1a, 0x02, 0x0e, 0x13, 0x8c, 0x8b, 0x80, 0x90,
+ 0xa5, 0x00, 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c,
+ 0x03, 0x0e, 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81,
+ 0xa0, 0x03, 0x0e, 0x00, 0x03, 0x81, 0x8e, 0x80,
+ 0xb8, 0x03, 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5,
+ 0x0d, 0x82, 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99,
+ 0x84, 0xca, 0x82, 0x8a, 0x86, 0x91, 0x8c, 0x92,
+ 0x8d, 0x91, 0x8d, 0x8c, 0x02, 0x8e, 0xb3, 0xa2,
+ 0x03, 0x80, 0xc2, 0xd8, 0x86, 0xa8, 0x00, 0x84,
+ 0xc5, 0x89, 0x9e, 0xb0, 0x9d, 0x0c, 0x8a, 0xab,
+ 0x83, 0x99, 0xb5, 0x96, 0x88, 0xb4, 0xd1, 0x80,
+ 0xdc, 0xae, 0x90, 0x87, 0xb5, 0x9d, 0x8c, 0x81,
+ 0x89, 0xab, 0x99, 0xa3, 0xa8, 0x82, 0x89, 0xa3,
+ 0x81, 0x88, 0x86, 0xaa, 0x0a, 0xa8, 0x18, 0x28,
+ 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d,
+ 0x81, 0xa5, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x80,
+ 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, 0x13,
+ 0x0d, 0x83, 0x8c, 0x22, 0x06, 0xf3, 0x80, 0x8c,
+ 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00,
+ 0x0d, 0x28, 0x00, 0x00, 0x80, 0x8f, 0x0b, 0x24,
+ 0x18, 0x90, 0xa8, 0x4a, 0x76, 0x40, 0xe4, 0x2b,
+ 0x11, 0x8b, 0xa5, 0x00, 0x20, 0x81, 0xb7, 0x30,
+ 0x8f, 0x96, 0x88, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x86, 0x42, 0x25, 0x82, 0x98, 0x88,
+ 0x34, 0x0c, 0x83, 0xd5, 0x1c, 0x80, 0xd9, 0x03,
+ 0x84, 0xaa, 0x80, 0xdd, 0x90, 0x9f, 0xaf, 0x8f,
+ 0x41, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x56, 0x8c,
+ 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89,
+ 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6,
+ 0x88, 0x81, 0xe6, 0x81, 0xbf, 0x21, 0x00, 0x04,
+ 0x97, 0x8f, 0x02, 0x03, 0x80, 0x96, 0x9c, 0xb3,
+ 0x8d, 0xb1, 0xbd, 0x2a, 0x00, 0x81, 0x8a, 0x9b,
+ 0x89, 0x96, 0x98, 0x9c, 0x86, 0xae, 0x9b, 0x80,
+ 0x8f, 0x20, 0x89, 0x89, 0x20, 0xa8, 0x96, 0x10,
+ 0x87, 0x93, 0x96, 0x10, 0x82, 0xb1, 0x00, 0x11,
+ 0x0c, 0x08, 0x00, 0x97, 0x11, 0x8a, 0x32, 0x8b,
+ 0x29, 0x29, 0x85, 0x88, 0x30, 0x30, 0xaa, 0x80,
+ 0x8d, 0x85, 0xf2, 0x9c, 0x60, 0x2b, 0xa3, 0x8b,
+ 0x96, 0x83, 0xb0, 0x60, 0x21, 0x03, 0x41, 0x6d,
+ 0x81, 0xe9, 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x89,
+ 0x80, 0x8c, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb,
+ 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7,
+ 0x8b, 0xf3, 0x20, 0x40, 0x86, 0xa3, 0x99, 0x85,
+ 0x99, 0x8a, 0xd8, 0x15, 0x0d, 0x0d, 0x0a, 0xa2,
+ 0x8b, 0x80, 0x99, 0x80, 0x92, 0x01, 0x80, 0x8e,
+ 0x81, 0x8d, 0xa1, 0xfa, 0xc4, 0xb4, 0x41, 0x0a,
+ 0x9c, 0x82, 0xb0, 0xae, 0x9f, 0x8c, 0x9d, 0x84,
+ 0xa5, 0x89, 0x9d, 0x81, 0xa3, 0x1f, 0x04, 0xa9,
+ 0x40, 0x9d, 0x91, 0xa3, 0x83, 0xa3, 0x83, 0xa7,
+ 0x87, 0xb3, 0x8b, 0x8a, 0x80, 0x8e, 0x06, 0x01,
+ 0x80, 0x8a, 0x80, 0x8e, 0x06, 0x01, 0xc2, 0x41,
+ 0x36, 0x88, 0x95, 0x89, 0x87, 0x97, 0x28, 0xa9,
+ 0x80, 0x88, 0xc4, 0x29, 0x00, 0xab, 0x01, 0x10,
+ 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, 0xc0, 0x92,
+ 0x01, 0x89, 0x95, 0x89, 0x99, 0xc5, 0xb7, 0x29,
+ 0xbf, 0x80, 0x8e, 0x18, 0x10, 0x9c, 0xa9, 0x9c,
+ 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, 0xb5, 0x89,
+ 0x95, 0x89, 0x92, 0x8c, 0x91, 0xed, 0xc8, 0xb6,
+ 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, 0x41, 0x5b, 0xa9,
+ 0x29, 0xcd, 0x9c, 0x89, 0x07, 0x95, 0xa9, 0x91,
+ 0xad, 0x94, 0x9a, 0x96, 0x8b, 0xb4, 0xb8, 0x09,
+ 0x80, 0x8c, 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c,
+ 0x01, 0x07, 0xa2, 0x10, 0x8b, 0xaf, 0x8d, 0x83,
+ 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0xd3,
+ 0x30, 0x00, 0x18, 0x8e, 0x80, 0x89, 0x86, 0xae,
+ 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, 0x10,
+ 0x91, 0x80, 0x8b, 0x84, 0x40, 0x9d, 0xb4, 0x91,
+ 0x83, 0x93, 0x82, 0x9d, 0xaf, 0x93, 0x08, 0x80,
+ 0x40, 0xb7, 0xae, 0xa8, 0x83, 0xa3, 0xaf, 0x93,
+ 0x80, 0xba, 0xaa, 0x8c, 0x80, 0xc6, 0x9a, 0xa4,
+ 0x86, 0x40, 0xb8, 0xab, 0xf3, 0xbf, 0x9e, 0x39,
+ 0x01, 0x38, 0x08, 0x97, 0x8e, 0x00, 0x80, 0xdd,
+ 0x39, 0xa6, 0x8f, 0x00, 0x80, 0x9b, 0x80, 0x89,
+ 0xa7, 0x30, 0x94, 0x80, 0x8a, 0xad, 0x92, 0x80,
+ 0x91, 0xc8, 0x41, 0x06, 0x88, 0x80, 0xa4, 0x90,
+ 0x80, 0xb0, 0x9d, 0xef, 0x30, 0x08, 0xa5, 0x94,
+ 0x80, 0x98, 0x28, 0x08, 0x9f, 0x8d, 0x80, 0x41,
+ 0x46, 0x92, 0x40, 0xbc, 0x80, 0xce, 0x43, 0x99,
+ 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, 0x4b, 0xe0,
+ 0x8e, 0x44, 0x2e, 0x4f, 0xd0, 0x42, 0x46, 0x60,
+ 0x21, 0xb8, 0x42, 0x38, 0x86, 0x9e, 0x90, 0xce,
+ 0x90, 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94,
+ 0x84, 0x92, 0x42, 0xaf, 0xbf, 0xff, 0xca, 0x20,
+ 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, 0x57, 0xf7,
+ 0x87, 0x44, 0xd5, 0xa9, 0x88, 0x60, 0x22, 0xe6,
+ 0x18, 0x30, 0x08, 0x41, 0x22, 0xac, 0x82, 0x90,
+ 0x1f, 0x41, 0x8b, 0x49, 0x03, 0xea, 0x84, 0x8c,
+ 0x82, 0x88, 0x86, 0x89, 0x57, 0x65, 0xd4, 0x80,
+ 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00,
+ 0x06, 0x80, 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b,
+ 0x03, 0x04, 0x00, 0x16, 0x80, 0x41, 0x53, 0x81,
+ 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80,
+ 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80,
+ 0x9e, 0x80, 0x98, 0x07, 0x47, 0x33, 0x9e, 0x41,
+ 0xe0, 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, 0x40,
+ 0x9d, 0x91, 0xab, 0x44, 0xf3, 0x30, 0x18, 0x08,
+ 0x8e, 0x80, 0x40, 0xc4, 0xba, 0xc3, 0x30, 0x44,
+ 0xb3, 0x18, 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89,
+ 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, 0x80, 0x89,
+ 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, 0x51, 0x43,
+ 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x38, 0x86, 0x40,
+ 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x4c,
+ 0x1e, 0x42, 0x1d, 0x45, 0xe1, 0x53, 0x4a,
+};
+
+static const uint8_t unicode_prop_ID_Start_index[102] = {
+ 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09,
+ 0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b,
+ 0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00,
+ 0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d,
+ 0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00,
+ 0x32, 0x00, 0xda, 0xa7, 0x00, 0x4c, 0xaa, 0x20,
+ 0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02,
+ 0x21, 0x96, 0x05, 0x01, 0xf3, 0x08, 0x01, 0xb3,
+ 0x0c, 0x21, 0x73, 0x11, 0x61, 0x3e, 0x13, 0x01,
+ 0x47, 0x17, 0x21, 0x9e, 0x1a, 0x01, 0x9a, 0x23,
+ 0x01, 0x78, 0x6b, 0x01, 0xfc, 0xb2, 0x61, 0x3a,
+ 0xd5, 0x01, 0x2d, 0xe1, 0x41, 0x33, 0xee, 0x01,
+ 0xe0, 0xa6, 0x62, 0x4b, 0x13, 0x03,
+};
+
+static const uint8_t unicode_prop_ID_Continue1_table[640] = {
+ 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47,
+ 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08,
+ 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf,
+ 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89,
+ 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89,
+ 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02,
+ 0x04, 0xaa, 0x82, 0xbb, 0x87, 0xa9, 0x97, 0x80,
+ 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, 0x09, 0x89,
+ 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88,
+ 0x80, 0x89, 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7,
+ 0x00, 0x23, 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10,
+ 0x8a, 0x82, 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93,
+ 0x09, 0x89, 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31,
+ 0x09, 0x16, 0x82, 0x89, 0x09, 0x89, 0x91, 0x80,
+ 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89,
+ 0x8f, 0x84, 0xb6, 0x00, 0x30, 0x10, 0x1e, 0x81,
+ 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x30,
+ 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x8f, 0x83,
+ 0xb6, 0x08, 0x30, 0x10, 0x83, 0x88, 0x80, 0x89,
+ 0x09, 0x89, 0x90, 0x82, 0xc5, 0x03, 0x28, 0x00,
+ 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, 0x38,
+ 0x89, 0xd6, 0x01, 0x88, 0x8a, 0x29, 0x89, 0xbd,
+ 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, 0xb0,
+ 0x93, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80,
+ 0xe3, 0x93, 0x80, 0x89, 0x8b, 0x1b, 0x10, 0x11,
+ 0x32, 0x83, 0x8c, 0x8b, 0x80, 0x8e, 0x42, 0xbe,
+ 0x82, 0x88, 0x88, 0x43, 0x9f, 0x83, 0x9b, 0x82,
+ 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x9f, 0x88, 0x01,
+ 0x89, 0xa0, 0x10, 0x8a, 0x40, 0x8e, 0x80, 0xf5,
+ 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, 0xbb,
+ 0x84, 0xb8, 0x89, 0x80, 0x9c, 0x81, 0x8a, 0x85,
+ 0x89, 0x95, 0x8d, 0x80, 0x8f, 0xb0, 0x84, 0xae,
+ 0x90, 0x8a, 0x89, 0x90, 0x88, 0x8b, 0x82, 0x9d,
+ 0x8c, 0x81, 0x89, 0xab, 0x8d, 0xaf, 0x93, 0x87,
+ 0x89, 0x85, 0x89, 0xf5, 0x10, 0x94, 0x18, 0x28,
+ 0x0a, 0x40, 0xc5, 0xbf, 0x42, 0x3e, 0x81, 0x92,
+ 0x80, 0xfa, 0x8c, 0x18, 0x82, 0x8b, 0x4b, 0xfd,
+ 0x82, 0x40, 0x8c, 0x80, 0xdf, 0x9f, 0x42, 0x29,
+ 0x85, 0xe8, 0x81, 0x60, 0x75, 0x84, 0x89, 0xc4,
+ 0x03, 0x89, 0x9f, 0x81, 0xcf, 0x81, 0x41, 0x0f,
+ 0x02, 0x03, 0x80, 0x96, 0x23, 0x80, 0xd2, 0x81,
+ 0xb1, 0x91, 0x89, 0x89, 0x85, 0x91, 0x8c, 0x8a,
+ 0x9b, 0x87, 0x98, 0x8c, 0xab, 0x83, 0xae, 0x8d,
+ 0x8e, 0x89, 0x8a, 0x80, 0x89, 0x89, 0xae, 0x8d,
+ 0x8b, 0x07, 0x09, 0x89, 0xa0, 0x82, 0xb1, 0x00,
+ 0x11, 0x0c, 0x08, 0x80, 0xa8, 0x24, 0x81, 0x40,
+ 0xeb, 0x38, 0x09, 0x89, 0x60, 0x4f, 0x23, 0x80,
+ 0x42, 0xe0, 0x8f, 0x8f, 0x8f, 0x11, 0x97, 0x82,
+ 0x40, 0xbf, 0x89, 0xa4, 0x80, 0x42, 0xbc, 0x80,
+ 0x40, 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, 0x24,
+ 0x89, 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, 0x13,
+ 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x1f, 0x89,
+ 0x41, 0x70, 0x81, 0x40, 0x98, 0x8a, 0xb0, 0x83,
+ 0xf9, 0x82, 0xb4, 0x8e, 0x9e, 0x8a, 0x09, 0x89,
+ 0x83, 0xac, 0x8a, 0x30, 0xac, 0x89, 0x2a, 0xa3,
+ 0x8d, 0x80, 0x89, 0x21, 0xab, 0x80, 0x8b, 0x82,
+ 0xaf, 0x8d, 0x3b, 0x80, 0x8b, 0xd1, 0x8b, 0x28,
+ 0x40, 0x9f, 0x8b, 0x84, 0x89, 0x2b, 0xb6, 0x08,
+ 0x31, 0x09, 0x82, 0x88, 0x80, 0x89, 0x09, 0x32,
+ 0x84, 0x40, 0xbf, 0x91, 0x88, 0x89, 0x18, 0xd0,
+ 0x93, 0x8b, 0x89, 0x40, 0xd4, 0x31, 0x88, 0x9a,
+ 0x81, 0xd1, 0x90, 0x8e, 0x89, 0xd0, 0x8c, 0x87,
+ 0x89, 0xd2, 0x8e, 0x83, 0x89, 0x40, 0xf1, 0x8e,
+ 0x40, 0xa4, 0x89, 0xc5, 0x28, 0x09, 0x18, 0x00,
+ 0x81, 0x8b, 0x89, 0xf6, 0x31, 0x32, 0x80, 0x9b,
+ 0x89, 0xa7, 0x30, 0x1f, 0x80, 0x88, 0x8a, 0xad,
+ 0x8f, 0x41, 0x94, 0x38, 0x87, 0x8f, 0x89, 0xb7,
+ 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, 0x30,
+ 0x07, 0x89, 0xaf, 0x20, 0x08, 0x27, 0x89, 0x41,
+ 0x48, 0x83, 0x60, 0x4b, 0x68, 0x89, 0xd5, 0x89,
+ 0xa5, 0x84, 0xba, 0x86, 0x98, 0x89, 0x43, 0xf4,
+ 0x00, 0xb6, 0x33, 0xd0, 0x80, 0x8a, 0x81, 0x60,
+ 0x4c, 0xaa, 0x81, 0x52, 0x60, 0xad, 0x81, 0x96,
+ 0x42, 0x1d, 0x22, 0x2f, 0x39, 0x86, 0x9d, 0x83,
+ 0x40, 0x93, 0x82, 0x45, 0x88, 0xb1, 0x41, 0xff,
+ 0xb6, 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20,
+ 0x8e, 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04,
+ 0x41, 0x04, 0x86, 0x88, 0x89, 0x41, 0x63, 0x80,
+ 0xbc, 0x8d, 0x45, 0xd5, 0x86, 0xec, 0x34, 0x89,
+ 0x52, 0x95, 0x89, 0x6c, 0x05, 0x05, 0x40, 0xef,
+};
+
+static const uint8_t unicode_prop_ID_Continue1_index[60] = {
+ 0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a,
+ 0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x40, 0xc7,
+ 0x0f, 0x00, 0xea, 0x17, 0x20, 0x45, 0x1b, 0x20,
+ 0x55, 0x20, 0x20, 0x0c, 0xa8, 0x60, 0x37, 0xaa,
+ 0x00, 0x50, 0xfe, 0x00, 0x3a, 0x0d, 0x01, 0x83,
+ 0x11, 0x01, 0xc4, 0x14, 0x21, 0x44, 0x19, 0x21,
+ 0x5a, 0x1d, 0x41, 0x9f, 0xbc, 0x61, 0xb0, 0xda,
+ 0x21, 0xf0, 0x01, 0x0e,
+};
+
+#ifdef CONFIG_ALL_UNICODE
+
+static const uint8_t unicode_cc_table[881] = {
+ 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00,
+ 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03,
+ 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03,
+ 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2,
+ 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00,
+ 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea,
+ 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0,
+ 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00,
+ 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05,
+ 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00,
+ 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00,
+ 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc,
+ 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e,
+ 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00,
+ 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0,
+ 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81,
+ 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2,
+ 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00,
+ 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0,
+ 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc,
+ 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0,
+ 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc,
+ 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4,
+ 0xaa, 0x02, 0xdc, 0xb0, 0x0b, 0xc0, 0x02, 0xdc,
+ 0xc3, 0xa9, 0xc4, 0x04, 0xdc, 0xcd, 0x80, 0x00,
+ 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2,
+ 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1,
+ 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, 0x07, 0x8f,
+ 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, 0xc1, 0xb0,
+ 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xaf, 0xc0,
+ 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0,
+ 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3d,
+ 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x4e, 0x00,
+ 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09,
+ 0x86, 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00,
+ 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09,
+ 0x8f, 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0,
+ 0x3c, 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b,
+ 0xb0, 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03,
+ 0x7a, 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc,
+ 0x80, 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06,
+ 0x41, 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82,
+ 0x81, 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80,
+ 0xc1, 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00,
+ 0x07, 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc,
+ 0xb2, 0x9e, 0xc2, 0xb3, 0x83, 0x01, 0x09, 0x9d,
+ 0x00, 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0,
+ 0xb0, 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde,
+ 0xc0, 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc,
+ 0xb0, 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00,
+ 0xdc, 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc,
+ 0x80, 0x01, 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0x00,
+ 0xdc, 0xc3, 0xb0, 0x34, 0x00, 0x07, 0x8e, 0x00,
+ 0x09, 0xa5, 0xc0, 0x00, 0xdc, 0xc6, 0xb0, 0x05,
+ 0x01, 0x09, 0xb0, 0x09, 0x00, 0x07, 0x8a, 0x01,
+ 0x09, 0xb0, 0x12, 0x00, 0x07, 0xb0, 0x67, 0xc2,
+ 0x41, 0x00, 0x04, 0xdc, 0xc1, 0x03, 0xdc, 0xc0,
+ 0x41, 0x00, 0x05, 0x01, 0x83, 0x00, 0xdc, 0x85,
+ 0xc0, 0x82, 0xc1, 0xb0, 0x95, 0xc1, 0x00, 0xdc,
+ 0xc6, 0x00, 0xdc, 0xc1, 0x00, 0xea, 0x00, 0xd6,
+ 0x00, 0xdc, 0x00, 0xca, 0xe4, 0x00, 0xe8, 0x01,
+ 0xe4, 0x00, 0xdc, 0x00, 0xda, 0xc0, 0x00, 0xe9,
+ 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xb2, 0x9f, 0xc1,
+ 0x01, 0x01, 0xc3, 0x02, 0x01, 0xc1, 0x83, 0xc0,
+ 0x82, 0x01, 0x01, 0xc0, 0x00, 0xdc, 0xc0, 0x01,
+ 0x01, 0x03, 0xdc, 0xc0, 0xb8, 0x03, 0xcd, 0xc2,
+ 0xb0, 0x5c, 0x00, 0x09, 0xb0, 0x2f, 0xdf, 0xb1,
+ 0xf9, 0x00, 0xda, 0x00, 0xe4, 0x00, 0xe8, 0x00,
+ 0xde, 0x01, 0xe0, 0xb0, 0x38, 0x01, 0x08, 0xb8,
+ 0x6d, 0xa3, 0xc0, 0x83, 0xc9, 0x9f, 0xc1, 0xb0,
+ 0x1f, 0xc1, 0xb0, 0xe3, 0x00, 0x09, 0xa4, 0x00,
+ 0x09, 0xb0, 0x66, 0x00, 0x09, 0x9a, 0xd1, 0xb0,
+ 0x08, 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, 0x2e,
+ 0x00, 0x07, 0x8b, 0x00, 0x09, 0xb0, 0xbe, 0xc0,
+ 0x80, 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, 0xc1,
+ 0x80, 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, 0xc5,
+ 0x00, 0x09, 0xb8, 0x46, 0xff, 0x00, 0x1a, 0xb2,
+ 0xd0, 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, 0x00,
+ 0xdc, 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, 0xc4,
+ 0xb6, 0x61, 0x00, 0xdc, 0x80, 0xc0, 0xa7, 0xc0,
+ 0x00, 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, 0xb0,
+ 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb1,
+ 0x52, 0xc1, 0xb0, 0x68, 0x01, 0xdc, 0xc2, 0x00,
+ 0xdc, 0xc0, 0x03, 0xdc, 0xb0, 0x00, 0xc0, 0x00,
+ 0xdc, 0xc0, 0x00, 0xdc, 0xb0, 0x8f, 0x00, 0x09,
+ 0xa8, 0x00, 0x09, 0x8d, 0x00, 0x09, 0xb0, 0x08,
+ 0x00, 0x09, 0x00, 0x07, 0xb0, 0x14, 0xc2, 0xaf,
+ 0x01, 0x09, 0xb0, 0x0d, 0x00, 0x07, 0xb0, 0x1b,
+ 0x00, 0x09, 0x88, 0x00, 0x07, 0xb0, 0x39, 0x00,
+ 0x09, 0x00, 0x07, 0xb0, 0x81, 0x00, 0x07, 0x00,
+ 0x09, 0xb0, 0x1f, 0x01, 0x07, 0x8f, 0x00, 0x09,
+ 0x97, 0xc6, 0x82, 0xc4, 0xb0, 0x9c, 0x00, 0x09,
+ 0x82, 0x00, 0x07, 0x96, 0xc0, 0xb0, 0x32, 0x00,
+ 0x09, 0x00, 0x07, 0xb0, 0xca, 0x00, 0x09, 0x00,
+ 0x07, 0xb0, 0x4d, 0x00, 0x09, 0xb0, 0x45, 0x00,
+ 0x09, 0x00, 0x07, 0xb0, 0x42, 0x00, 0x09, 0xb0,
+ 0xdc, 0x00, 0x09, 0x00, 0x07, 0xb0, 0xd1, 0x01,
+ 0x09, 0x83, 0x00, 0x07, 0xb0, 0x6b, 0x00, 0x09,
+ 0xb0, 0x22, 0x00, 0x09, 0x91, 0x00, 0x09, 0xb0,
+ 0x20, 0x00, 0x09, 0xb1, 0x74, 0x00, 0x09, 0xb0,
+ 0xd1, 0x00, 0x07, 0x80, 0x01, 0x09, 0xb0, 0x20,
+ 0x00, 0x09, 0xb8, 0x45, 0x27, 0x04, 0x01, 0xb0,
+ 0x0a, 0xc6, 0xb4, 0x88, 0x01, 0x06, 0xb8, 0x44,
+ 0x7b, 0x00, 0x01, 0xb8, 0x0c, 0x95, 0x01, 0xd8,
+ 0x02, 0x01, 0x82, 0x00, 0xe2, 0x04, 0xd8, 0x87,
+ 0x07, 0xdc, 0x81, 0xc4, 0x01, 0xdc, 0x9d, 0xc3,
+ 0xb0, 0x63, 0xc2, 0xb8, 0x05, 0x8a, 0xc6, 0x80,
+ 0xd0, 0x81, 0xc6, 0x80, 0xc1, 0x80, 0xc4, 0xb0,
+ 0xd4, 0xc6, 0xb1, 0x46, 0xc0, 0xb0, 0x0c, 0xc3,
+ 0xb5, 0xaf, 0x06, 0xdc, 0xb0, 0x3c, 0xc5, 0x00,
+ 0x07,
+};
+
+static const uint8_t unicode_cc_index[84] = {
+ 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05,
+ 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c,
+ 0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00,
+ 0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10,
+ 0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3,
+ 0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00,
+ 0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab,
+ 0x00, 0x39, 0x0a, 0x01, 0x84, 0x0f, 0x21, 0xc0,
+ 0x11, 0x01, 0x43, 0x14, 0x01, 0x39, 0x18, 0x21,
+ 0x42, 0x1d, 0x21, 0x67, 0xd1, 0x01, 0x30, 0xe1,
+ 0x21, 0x4b, 0xe9, 0x01,
+};
+
+static const uint32_t unicode_decomp_table1[693] = {
+ 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097,
+ 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097,
+ 0x002e4115, 0x002f0199, 0x00302016, 0x00400842,
+ 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117,
+ 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342,
+ 0x005240bf, 0x00530342, 0x00550942, 0x005a0842,
+ 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142,
+ 0x006bc142, 0x00710185, 0x0071c317, 0x00734844,
+ 0x00778344, 0x00798342, 0x007b02be, 0x007c4197,
+ 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142,
+ 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283,
+ 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097,
+ 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080,
+ 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad,
+ 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081,
+ 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be,
+ 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be,
+ 0x011d8144, 0x01304144, 0x01340244, 0x01358144,
+ 0x01368344, 0x01388344, 0x013a8644, 0x013e0144,
+ 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184,
+ 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084,
+ 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084,
+ 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084,
+ 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084,
+ 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122,
+ 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae,
+ 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081,
+ 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084,
+ 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e,
+ 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084,
+ 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084,
+ 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081,
+ 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120,
+ 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783,
+ 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942,
+ 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085,
+ 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122,
+ 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122,
+ 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e,
+ 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122,
+ 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be,
+ 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be,
+ 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be,
+ 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820,
+ 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080,
+ 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117,
+ 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080,
+ 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080,
+ 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080,
+ 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080,
+ 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080,
+ 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081,
+ 0x0805c097, 0x08090081, 0x08094097, 0x08098099,
+ 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085,
+ 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3,
+ 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215,
+ 0x0820051f, 0x08228583, 0x08254415, 0x082a0097,
+ 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119,
+ 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081,
+ 0x08458097, 0x08464295, 0x08480097, 0x08484099,
+ 0x08488097, 0x08490081, 0x08498080, 0x084a0081,
+ 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081,
+ 0x084ec099, 0x084f0283, 0x08514295, 0x08540119,
+ 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081,
+ 0x08584097, 0x08588099, 0x0858c097, 0x08590081,
+ 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097,
+ 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295,
+ 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081,
+ 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097,
+ 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215,
+ 0x08624099, 0x0866813e, 0x086b80be, 0x087341be,
+ 0x088100be, 0x088240be, 0x088300be, 0x088901be,
+ 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1,
+ 0x089040be, 0x089100be, 0x0891c1be, 0x089801be,
+ 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144,
+ 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244,
+ 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523,
+ 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b,
+ 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25,
+ 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3,
+ 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be,
+ 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081,
+ 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383,
+ 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081,
+ 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122,
+ 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122,
+ 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085,
+ 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122,
+ 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084,
+ 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d,
+ 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703,
+ 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9,
+ 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089,
+ 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203,
+ 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d,
+ 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3,
+ 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523,
+ 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097,
+ 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad,
+ 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3,
+ 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1,
+ 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1,
+ 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7,
+ 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3,
+ 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3,
+ 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5,
+ 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1,
+ 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5,
+ 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1,
+ 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1,
+ 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3,
+ 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133,
+ 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1,
+ 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1,
+ 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099,
+ 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127,
+ 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099,
+ 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205,
+ 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1,
+ 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097,
+ 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f,
+ 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b,
+ 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117,
+ 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099,
+ 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525,
+ 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103,
+ 0x29dc0081, 0x29fc8195, 0x29fe0103, 0x2ad70203,
+ 0x2ada4081, 0x3e401482, 0x3e4a7f82, 0x3e6a3f82,
+ 0x3e8aa102, 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590,
+ 0x3ec00197, 0x3ec0c119, 0x3ec1413f, 0x3ec4c2af,
+ 0x3ec74184, 0x3ec804ad, 0x3eca4081, 0x3eca8304,
+ 0x3ecc03a0, 0x3ece02a0, 0x3ecf8084, 0x3ed00120,
+ 0x3ed0c120, 0x3ed184ae, 0x3ed3c085, 0x3ed4312d,
+ 0x3ef4cbad, 0x3efa892f, 0x3eff022d, 0x3f002f2f,
+ 0x3f1782a5, 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf,
+ 0x3f3c81a5, 0x3f3d64af, 0x3f542031, 0x3f649b31,
+ 0x3f7c0131, 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd,
+ 0x3f7ec0bb, 0x3f7f00b3, 0x3f840503, 0x3f8c01ad,
+ 0x3f8cc315, 0x3f8e462d, 0x3f91cc03, 0x3f97c695,
+ 0x3f9c01af, 0x3f9d0085, 0x3f9d852f, 0x3fa03aad,
+ 0x3fbd442f, 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad,
+ 0x3fe80081, 0x3fe84f1f, 0x3ff0831f, 0x3ff2831f,
+ 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x41e04d83,
+ 0x41e70f91, 0x44268192, 0x442ac092, 0x444b8112,
+ 0x44d2c112, 0x452ec212, 0x456e8112, 0x464e0092,
+ 0x74578392, 0x746ec312, 0x75000d1f, 0x75068d1f,
+ 0x750d0d1f, 0x7513839f, 0x7515891f, 0x751a0d1f,
+ 0x75208d1f, 0x75271015, 0x752f439f, 0x7531459f,
+ 0x75340d1f, 0x753a8d1f, 0x75410395, 0x7543441f,
+ 0x7545839f, 0x75478d1f, 0x754e0795, 0x7552839f,
+ 0x75548d1f, 0x755b0d1f, 0x75618d1f, 0x75680d1f,
+ 0x756e8d1f, 0x75750d1f, 0x757b8d1f, 0x75820d1f,
+ 0x75888d1f, 0x758f0d1f, 0x75958d1f, 0x759c0d1f,
+ 0x75a28d1f, 0x75a90103, 0x75aa089f, 0x75ae4081,
+ 0x75ae839f, 0x75b04081, 0x75b08c9f, 0x75b6c081,
+ 0x75b7032d, 0x75b8889f, 0x75bcc081, 0x75bd039f,
+ 0x75bec081, 0x75bf0c9f, 0x75c54081, 0x75c5832d,
+ 0x75c7089f, 0x75cb4081, 0x75cb839f, 0x75cd4081,
+ 0x75cd8c9f, 0x75d3c081, 0x75d4032d, 0x75d5889f,
+ 0x75d9c081, 0x75da039f, 0x75dbc081, 0x75dc0c9f,
+ 0x75e24081, 0x75e2832d, 0x75e4089f, 0x75e84081,
+ 0x75e8839f, 0x75ea4081, 0x75ea8c9f, 0x75f0c081,
+ 0x75f1042d, 0x75f3851f, 0x75f6051f, 0x75f8851f,
+ 0x75fb051f, 0x75fd851f, 0x7b80022d, 0x7b814dad,
+ 0x7b884203, 0x7b89c081, 0x7b8a452d, 0x7b8d0403,
+ 0x7b908081, 0x7b91dc03, 0x7ba0052d, 0x7ba2c8ad,
+ 0x7ba84483, 0x7baac8ad, 0x7c400097, 0x7c404521,
+ 0x7c440d25, 0x7c4a8087, 0x7c4ac115, 0x7c4b4117,
+ 0x7c4c0d1f, 0x7c528217, 0x7c538099, 0x7c53c097,
+ 0x7c5a8197, 0x7c640097, 0x7c80012f, 0x7c808081,
+ 0x7c841603, 0x7c9004c1, 0x7c940103, 0x7efc051f,
+ 0xbe0001ac, 0xbe00d110, 0xbe0947ac, 0xbe0d3910,
+ 0xbe29872c, 0xbe2d022c, 0xbe2e3790, 0xbe49ff90,
+ 0xbe69bc10,
+};
+
+static const uint16_t unicode_decomp_table2[693] = {
+ 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008,
+ 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3,
+ 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8,
+ 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c,
+ 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192,
+ 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7,
+ 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218,
+ 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b,
+ 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275,
+ 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9,
+ 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1,
+ 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317,
+ 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341,
+ 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363,
+ 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385,
+ 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc,
+ 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1,
+ 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c,
+ 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614,
+ 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e,
+ 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9,
+ 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705,
+ 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737,
+ 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a,
+ 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c,
+ 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010,
+ 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db,
+ 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801,
+ 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830,
+ 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e,
+ 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069,
+ 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049,
+ 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4,
+ 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076,
+ 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7,
+ 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9,
+ 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923,
+ 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956,
+ 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978,
+ 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993,
+ 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f,
+ 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020,
+ 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1,
+ 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39,
+ 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59,
+ 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec,
+ 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a,
+ 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc,
+ 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c,
+ 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e,
+ 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca,
+ 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04,
+ 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45,
+ 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76,
+ 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad,
+ 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5,
+ 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a,
+ 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f,
+ 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1,
+ 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2,
+ 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153,
+ 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181,
+ 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab,
+ 0xa76f, 0x11af, 0x11b2, 0x11b6, 0x028d, 0x11be, 0x1210, 0x130e,
+ 0x140c, 0x1490, 0x1495, 0x1553, 0x156c, 0x1572, 0x1578, 0x157e,
+ 0x158a, 0x1596, 0x002b, 0x15a1, 0x15b9, 0x15bd, 0x15c1, 0x15c5,
+ 0x15c9, 0x15cd, 0x15e1, 0x15e5, 0x1649, 0x1662, 0x1688, 0x168e,
+ 0x174c, 0x1752, 0x1757, 0x1777, 0x1877, 0x187d, 0x1911, 0x19d3,
+ 0x1a77, 0x1a7f, 0x1a9d, 0x1aa2, 0x1ab6, 0x1ac0, 0x1ac6, 0x1ada,
+ 0x1adf, 0x1ae5, 0x1af3, 0x1b23, 0x1b30, 0x1b38, 0x1b3c, 0x1b52,
+ 0x1bc9, 0x1bdb, 0x1bdd, 0x1bdf, 0x3164, 0x1c20, 0x1c22, 0x1c24,
+ 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c7e, 0x1cc4, 0x1cd2, 0x1cd7,
+ 0x1ce0, 0x1ce9, 0x1cfb, 0x1d04, 0x1d09, 0x1d29, 0x1d44, 0x1d46,
+ 0x1d48, 0x1d4a, 0x1d4c, 0x1d4e, 0x1d50, 0x1d52, 0x1d72, 0x1d74,
+ 0x1d76, 0x1d78, 0x1d7a, 0x1d81, 0x1d83, 0x1d85, 0x1d87, 0x1d96,
+ 0x1d98, 0x1d9a, 0x1d9c, 0x1d9e, 0x1da0, 0x1da2, 0x1da4, 0x1da6,
+ 0x1da8, 0x1daa, 0x1dac, 0x1dae, 0x1db0, 0x1db2, 0x1db6, 0x03f4,
+ 0x1db8, 0x2207, 0x1dba, 0x2202, 0x1dbc, 0x1dc4, 0x03f4, 0x1dc6,
+ 0x2207, 0x1dc8, 0x2202, 0x1dca, 0x1dd2, 0x03f4, 0x1dd4, 0x2207,
+ 0x1dd6, 0x2202, 0x1dd8, 0x1de0, 0x03f4, 0x1de2, 0x2207, 0x1de4,
+ 0x2202, 0x1de6, 0x1dee, 0x03f4, 0x1df0, 0x2207, 0x1df2, 0x2202,
+ 0x1df4, 0x1dfe, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0e,
+ 0x1e2b, 0x062d, 0x1e33, 0x1e3f, 0x062c, 0x1e4f, 0x1ebf, 0x1ecb,
+ 0x1ede, 0x1ef0, 0x1f03, 0x1f05, 0x1f09, 0x1f0f, 0x1f15, 0x1f17,
+ 0x1f1b, 0x1f1d, 0x1f25, 0x1f28, 0x1f2a, 0x1f30, 0x1f32, 0x30b5,
+ 0x1f38, 0x1f90, 0x1fa6, 0x1faa, 0x1fac, 0x1fb1, 0x1ffe, 0x200f,
+ 0x2110, 0x2120, 0x2126, 0x2220, 0x233e,
+};
+
+static const uint8_t unicode_decomp_data[9292] = {
+ 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81,
+ 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31,
+ 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41,
+ 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41,
+ 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45,
+ 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49,
+ 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e,
+ 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f,
+ 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55,
+ 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59,
+ 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61,
+ 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61,
+ 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65,
+ 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69,
+ 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e,
+ 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f,
+ 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75,
+ 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79,
+ 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41,
+ 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43,
+ 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45,
+ 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47,
+ 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48,
+ 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49,
+ 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a,
+ 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c,
+ 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e,
+ 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e,
+ 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81,
+ 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82,
+ 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c,
+ 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a,
+ 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82,
+ 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c,
+ 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01,
+ 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01,
+ 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a,
+ 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49,
+ 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c,
+ 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00,
+ 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26,
+ 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b,
+ 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01,
+ 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a,
+ 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a,
+ 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81,
+ 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f,
+ 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f,
+ 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f,
+ 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6,
+ 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45,
+ 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84,
+ 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00,
+ 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72,
+ 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77,
+ 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20,
+ 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63,
+ 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95,
+ 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20,
+ 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03,
+ 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99,
+ 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81,
+ 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03,
+ 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07,
+ 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00,
+ 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07,
+ 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00,
+ 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1,
+ 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba,
+ 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98,
+ 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04,
+ 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06,
+ 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80,
+ 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04,
+ 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00,
+ 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88,
+ 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04,
+ 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10,
+ 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86,
+ 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04,
+ 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e,
+ 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88,
+ 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04,
+ 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65,
+ 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00,
+ 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27,
+ 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23,
+ 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54,
+ 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c,
+ 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00,
+ 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c,
+ 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe,
+ 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc,
+ 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c,
+ 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00,
+ 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c,
+ 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09,
+ 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92,
+ 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09,
+ 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf,
+ 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2,
+ 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08,
+ 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca,
+ 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f,
+ 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2,
+ 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42,
+ 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51,
+ 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b,
+ 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71,
+ 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41,
+ 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80,
+ 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80,
+ 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7,
+ 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7,
+ 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5,
+ 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35,
+ 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35,
+ 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35,
+ 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00,
+ 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47,
+ 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52,
+ 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61,
+ 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62,
+ 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b,
+ 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b,
+ 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54,
+ 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74,
+ 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76,
+ 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4,
+ 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72,
+ 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3,
+ 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52,
+ 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c,
+ 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65,
+ 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b,
+ 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f,
+ 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73,
+ 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82,
+ 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a,
+ 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a,
+ 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8,
+ 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42,
+ 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81,
+ 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00,
+ 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12,
+ 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad,
+ 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00,
+ 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48,
+ 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7,
+ 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00,
+ 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b,
+ 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84,
+ 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87,
+ 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1,
+ 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88,
+ 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00,
+ 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52,
+ 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1,
+ 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01,
+ 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54,
+ 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1,
+ 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00,
+ 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a,
+ 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80,
+ 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3,
+ 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82,
+ 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88,
+ 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02,
+ 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00,
+ 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2,
+ 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82,
+ 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01,
+ 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45,
+ 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83,
+ 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00,
+ 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49,
+ 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3,
+ 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00,
+ 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc,
+ 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80,
+ 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01,
+ 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf,
+ 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89,
+ 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00,
+ 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59,
+ 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f,
+ 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91,
+ 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f,
+ 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03,
+ 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03,
+ 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81,
+ 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f,
+ 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21,
+ 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2,
+ 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f,
+ 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29,
+ 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2,
+ 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f,
+ 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31,
+ 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2,
+ 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f,
+ 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39,
+ 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2,
+ 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f,
+ 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03,
+ 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03,
+ 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81,
+ 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00,
+ 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59,
+ 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2,
+ 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f,
+ 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61,
+ 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2,
+ 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f,
+ 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69,
+ 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2,
+ 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03,
+ 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5,
+ 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45,
+ 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45,
+ 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70,
+ 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5,
+ 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f,
+ 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91,
+ 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20,
+ 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f,
+ 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00,
+ 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5,
+ 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03,
+ 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf,
+ 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84,
+ 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca,
+ 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe,
+ 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2,
+ 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03,
+ 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5,
+ 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5,
+ 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85,
+ 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03,
+ 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9,
+ 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80,
+ 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94,
+ 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20,
+ 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35,
+ 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00,
+ 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f,
+ 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69,
+ 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b,
+ 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29,
+ 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f,
+ 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c,
+ 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61,
+ 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43,
+ 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00,
+ 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20,
+ 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50,
+ 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45,
+ 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42,
+ 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f,
+ 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3,
+ 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44,
+ 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31,
+ 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0,
+ 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32,
+ 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35,
+ 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0,
+ 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37,
+ 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49,
+ 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49,
+ 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76,
+ 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69,
+ 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c,
+ 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21,
+ 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0,
+ 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8,
+ 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22,
+ 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25,
+ 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22,
+ 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e,
+ 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43,
+ 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00,
+ 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00,
+ 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c,
+ 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8,
+ 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22,
+ 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86,
+ 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8,
+ 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22,
+ 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03,
+ 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00,
+ 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00,
+ 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00,
+ 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00,
+ 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30,
+ 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41,
+ 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00,
+ 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e,
+ 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0,
+ 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57,
+ 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e,
+ 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6,
+ 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f,
+ 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15,
+ 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80,
+ 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38,
+ 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5,
+ 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a,
+ 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b,
+ 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73,
+ 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b,
+ 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97,
+ 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5,
+ 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20,
+ 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb,
+ 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14,
+ 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36,
+ 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59,
+ 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89,
+ 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f,
+ 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92,
+ 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf,
+ 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3,
+ 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74,
+ 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8,
+ 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd,
+ 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33,
+ 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00,
+ 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f,
+ 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c,
+ 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2,
+ 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55,
+ 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70,
+ 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b,
+ 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49,
+ 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77,
+ 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9,
+ 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62,
+ 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3,
+ 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf,
+ 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8,
+ 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f,
+ 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5,
+ 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00,
+ 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e,
+ 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12,
+ 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45,
+ 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99,
+ 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99,
+ 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99,
+ 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99,
+ 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99,
+ 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99,
+ 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99,
+ 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99,
+ 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99,
+ 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99,
+ 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8,
+ 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac,
+ 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09,
+ 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01,
+ 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08,
+ 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13,
+ 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22,
+ 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45,
+ 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d,
+ 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56,
+ 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75,
+ 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59,
+ 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00,
+ 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11,
+ 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11,
+ 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11,
+ 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11,
+ 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11,
+ 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11,
+ 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11,
+ 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00,
+ 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e,
+ 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e,
+ 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67,
+ 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91,
+ 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67,
+ 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c,
+ 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54,
+ 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c,
+ 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81,
+ 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65,
+ 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31,
+ 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00,
+ 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00,
+ 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06,
+ 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c,
+ 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e,
+ 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63,
+ 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e,
+ 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e,
+ 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e,
+ 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c,
+ 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65,
+ 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54,
+ 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52,
+ 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90,
+ 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98,
+ 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e,
+ 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53,
+ 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76,
+ 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59,
+ 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00,
+ 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00,
+ 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72,
+ 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30,
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d,
+ 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d,
+ 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d,
+ 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4,
+ 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b,
+ 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39,
+ 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30,
+ 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f,
+ 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11,
+ 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03,
+ 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58,
+ 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e,
+ 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30,
+ 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30,
+ 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad,
+ 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13,
+ 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00,
+ 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40,
+ 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40,
+ 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30,
+ 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b,
+ 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e,
+ 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02,
+ 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11,
+ 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c,
+ 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00,
+ 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f,
+ 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12,
+ 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a,
+ 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16,
+ 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38,
+ 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2,
+ 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30,
+ 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20,
+ 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28,
+ 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30,
+ 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00,
+ 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d,
+ 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22,
+ 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01,
+ 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30,
+ 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23,
+ 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14,
+ 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a,
+ 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30,
+ 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47,
+ 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c,
+ 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30,
+ 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c,
+ 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35,
+ 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2,
+ 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44,
+ 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28,
+ 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00,
+ 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec,
+ 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16,
+ 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30,
+ 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9,
+ 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68,
+ 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61,
+ 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64,
+ 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55,
+ 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c,
+ 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb,
+ 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e,
+ 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc,
+ 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41,
+ 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00,
+ 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c,
+ 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03,
+ 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b,
+ 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a,
+ 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48,
+ 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13,
+ 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13,
+ 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc,
+ 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d,
+ 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f,
+ 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63,
+ 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00,
+ 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00,
+ 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22,
+ 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00,
+ 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d,
+ 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64,
+ 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61,
+ 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2,
+ 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc,
+ 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56,
+ 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00,
+ 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70,
+ 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57,
+ 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00,
+ 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9,
+ 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63,
+ 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43,
+ 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61,
+ 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d,
+ 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f,
+ 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c,
+ 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d,
+ 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72,
+ 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41,
+ 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00,
+ 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00,
+ 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65,
+ 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x43,
+ 0x46, 0x51, 0x26, 0x01, 0x53, 0x01, 0x27, 0xa7,
+ 0x37, 0xab, 0x6b, 0x02, 0x52, 0xab, 0x48, 0x8c,
+ 0xf4, 0x66, 0xca, 0x8e, 0xc8, 0x8c, 0xd1, 0x6e,
+ 0x32, 0x4e, 0xe5, 0x53, 0x9c, 0x9f, 0x9c, 0x9f,
+ 0x51, 0x59, 0xd1, 0x91, 0x87, 0x55, 0x48, 0x59,
+ 0xf6, 0x61, 0x69, 0x76, 0x85, 0x7f, 0x3f, 0x86,
+ 0xba, 0x87, 0xf8, 0x88, 0x8f, 0x90, 0x02, 0x6a,
+ 0x1b, 0x6d, 0xd9, 0x70, 0xde, 0x73, 0x3d, 0x84,
+ 0x6a, 0x91, 0xf1, 0x99, 0x82, 0x4e, 0x75, 0x53,
+ 0x04, 0x6b, 0x1b, 0x72, 0x2d, 0x86, 0x1e, 0x9e,
+ 0x50, 0x5d, 0xeb, 0x6f, 0xcd, 0x85, 0x64, 0x89,
+ 0xc9, 0x62, 0xd8, 0x81, 0x1f, 0x88, 0xca, 0x5e,
+ 0x17, 0x67, 0x6a, 0x6d, 0xfc, 0x72, 0xce, 0x90,
+ 0x86, 0x4f, 0xb7, 0x51, 0xde, 0x52, 0xc4, 0x64,
+ 0xd3, 0x6a, 0x10, 0x72, 0xe7, 0x76, 0x01, 0x80,
+ 0x06, 0x86, 0x5c, 0x86, 0xef, 0x8d, 0x32, 0x97,
+ 0x6f, 0x9b, 0xfa, 0x9d, 0x8c, 0x78, 0x7f, 0x79,
+ 0xa0, 0x7d, 0xc9, 0x83, 0x04, 0x93, 0x7f, 0x9e,
+ 0xd6, 0x8a, 0xdf, 0x58, 0x04, 0x5f, 0x60, 0x7c,
+ 0x7e, 0x80, 0x62, 0x72, 0xca, 0x78, 0xc2, 0x8c,
+ 0xf7, 0x96, 0xd8, 0x58, 0x62, 0x5c, 0x13, 0x6a,
+ 0xda, 0x6d, 0x0f, 0x6f, 0x2f, 0x7d, 0x37, 0x7e,
+ 0x4b, 0x96, 0xd2, 0x52, 0x8b, 0x80, 0xdc, 0x51,
+ 0xcc, 0x51, 0x1c, 0x7a, 0xbe, 0x7d, 0xf1, 0x83,
+ 0x75, 0x96, 0x80, 0x8b, 0xcf, 0x62, 0x02, 0x6a,
+ 0xfe, 0x8a, 0x39, 0x4e, 0xe7, 0x5b, 0x12, 0x60,
+ 0x87, 0x73, 0x70, 0x75, 0x17, 0x53, 0xfb, 0x78,
+ 0xbf, 0x4f, 0xa9, 0x5f, 0x0d, 0x4e, 0xcc, 0x6c,
+ 0x78, 0x65, 0x22, 0x7d, 0xc3, 0x53, 0x5e, 0x58,
+ 0x01, 0x77, 0x49, 0x84, 0xaa, 0x8a, 0xba, 0x6b,
+ 0xb0, 0x8f, 0x88, 0x6c, 0xfe, 0x62, 0xe5, 0x82,
+ 0xa0, 0x63, 0x65, 0x75, 0xae, 0x4e, 0x69, 0x51,
+ 0xc9, 0x51, 0x81, 0x68, 0xe7, 0x7c, 0x6f, 0x82,
+ 0xd2, 0x8a, 0xcf, 0x91, 0xf5, 0x52, 0x42, 0x54,
+ 0x73, 0x59, 0xec, 0x5e, 0xc5, 0x65, 0xfe, 0x6f,
+ 0x2a, 0x79, 0xad, 0x95, 0x6a, 0x9a, 0x97, 0x9e,
+ 0xce, 0x9e, 0x9b, 0x52, 0xc6, 0x66, 0x77, 0x6b,
+ 0x62, 0x8f, 0x74, 0x5e, 0x90, 0x61, 0x00, 0x62,
+ 0x9a, 0x64, 0x23, 0x6f, 0x49, 0x71, 0x89, 0x74,
+ 0xca, 0x79, 0xf4, 0x7d, 0x6f, 0x80, 0x26, 0x8f,
+ 0xee, 0x84, 0x23, 0x90, 0x4a, 0x93, 0x17, 0x52,
+ 0xa3, 0x52, 0xbd, 0x54, 0xc8, 0x70, 0xc2, 0x88,
+ 0xaa, 0x8a, 0xc9, 0x5e, 0xf5, 0x5f, 0x7b, 0x63,
+ 0xae, 0x6b, 0x3e, 0x7c, 0x75, 0x73, 0xe4, 0x4e,
+ 0xf9, 0x56, 0xe7, 0x5b, 0xba, 0x5d, 0x1c, 0x60,
+ 0xb2, 0x73, 0x69, 0x74, 0x9a, 0x7f, 0x46, 0x80,
+ 0x34, 0x92, 0xf6, 0x96, 0x48, 0x97, 0x18, 0x98,
+ 0x8b, 0x4f, 0xae, 0x79, 0xb4, 0x91, 0xb8, 0x96,
+ 0xe1, 0x60, 0x86, 0x4e, 0xda, 0x50, 0xee, 0x5b,
+ 0x3f, 0x5c, 0x99, 0x65, 0x02, 0x6a, 0xce, 0x71,
+ 0x42, 0x76, 0xfc, 0x84, 0x7c, 0x90, 0x8d, 0x9f,
+ 0x88, 0x66, 0x2e, 0x96, 0x89, 0x52, 0x7b, 0x67,
+ 0xf3, 0x67, 0x41, 0x6d, 0x9c, 0x6e, 0x09, 0x74,
+ 0x59, 0x75, 0x6b, 0x78, 0x10, 0x7d, 0x5e, 0x98,
+ 0x6d, 0x51, 0x2e, 0x62, 0x78, 0x96, 0x2b, 0x50,
+ 0x19, 0x5d, 0xea, 0x6d, 0x2a, 0x8f, 0x8b, 0x5f,
+ 0x44, 0x61, 0x17, 0x68, 0x87, 0x73, 0x86, 0x96,
+ 0x29, 0x52, 0x0f, 0x54, 0x65, 0x5c, 0x13, 0x66,
+ 0x4e, 0x67, 0xa8, 0x68, 0xe5, 0x6c, 0x06, 0x74,
+ 0xe2, 0x75, 0x79, 0x7f, 0xcf, 0x88, 0xe1, 0x88,
+ 0xcc, 0x91, 0xe2, 0x96, 0x3f, 0x53, 0xba, 0x6e,
+ 0x1d, 0x54, 0xd0, 0x71, 0x98, 0x74, 0xfa, 0x85,
+ 0xa3, 0x96, 0x57, 0x9c, 0x9f, 0x9e, 0x97, 0x67,
+ 0xcb, 0x6d, 0xe8, 0x81, 0xcb, 0x7a, 0x20, 0x7b,
+ 0x92, 0x7c, 0xc0, 0x72, 0x99, 0x70, 0x58, 0x8b,
+ 0xc0, 0x4e, 0x36, 0x83, 0x3a, 0x52, 0x07, 0x52,
+ 0xa6, 0x5e, 0xd3, 0x62, 0xd6, 0x7c, 0x85, 0x5b,
+ 0x1e, 0x6d, 0xb4, 0x66, 0x3b, 0x8f, 0x4c, 0x88,
+ 0x4d, 0x96, 0x8b, 0x89, 0xd3, 0x5e, 0x40, 0x51,
+ 0xc0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x58,
+ 0x00, 0x00, 0x74, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0xde, 0x51, 0x2a, 0x73, 0xca, 0x76, 0x3c, 0x79,
+ 0x5e, 0x79, 0x65, 0x79, 0x8f, 0x79, 0x56, 0x97,
+ 0xbe, 0x7c, 0xbd, 0x7f, 0x00, 0x00, 0x12, 0x86,
+ 0x00, 0x00, 0xf8, 0x8a, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x90, 0xfd, 0x90, 0xef, 0x98, 0xfc, 0x98,
+ 0x28, 0x99, 0xb4, 0x9d, 0xde, 0x90, 0xb7, 0x96,
+ 0xae, 0x4f, 0xe7, 0x50, 0x4d, 0x51, 0xc9, 0x52,
+ 0xe4, 0x52, 0x51, 0x53, 0x9d, 0x55, 0x06, 0x56,
+ 0x68, 0x56, 0x40, 0x58, 0xa8, 0x58, 0x64, 0x5c,
+ 0x6e, 0x5c, 0x94, 0x60, 0x68, 0x61, 0x8e, 0x61,
+ 0xf2, 0x61, 0x4f, 0x65, 0xe2, 0x65, 0x91, 0x66,
+ 0x85, 0x68, 0x77, 0x6d, 0x1a, 0x6e, 0x22, 0x6f,
+ 0x6e, 0x71, 0x2b, 0x72, 0x22, 0x74, 0x91, 0x78,
+ 0x3e, 0x79, 0x49, 0x79, 0x48, 0x79, 0x50, 0x79,
+ 0x56, 0x79, 0x5d, 0x79, 0x8d, 0x79, 0x8e, 0x79,
+ 0x40, 0x7a, 0x81, 0x7a, 0xc0, 0x7b, 0xf4, 0x7d,
+ 0x09, 0x7e, 0x41, 0x7e, 0x72, 0x7f, 0x05, 0x80,
+ 0xed, 0x81, 0x79, 0x82, 0x79, 0x82, 0x57, 0x84,
+ 0x10, 0x89, 0x96, 0x89, 0x01, 0x8b, 0x39, 0x8b,
+ 0xd3, 0x8c, 0x08, 0x8d, 0xb6, 0x8f, 0x38, 0x90,
+ 0xe3, 0x96, 0xff, 0x97, 0x3b, 0x98, 0x75, 0x60,
+ 0xee, 0x42, 0x18, 0x82, 0x02, 0x26, 0x4e, 0xb5,
+ 0x51, 0x68, 0x51, 0x80, 0x4f, 0x45, 0x51, 0x80,
+ 0x51, 0xc7, 0x52, 0xfa, 0x52, 0x9d, 0x55, 0x55,
+ 0x55, 0x99, 0x55, 0xe2, 0x55, 0x5a, 0x58, 0xb3,
+ 0x58, 0x44, 0x59, 0x54, 0x59, 0x62, 0x5a, 0x28,
+ 0x5b, 0xd2, 0x5e, 0xd9, 0x5e, 0x69, 0x5f, 0xad,
+ 0x5f, 0xd8, 0x60, 0x4e, 0x61, 0x08, 0x61, 0x8e,
+ 0x61, 0x60, 0x61, 0xf2, 0x61, 0x34, 0x62, 0xc4,
+ 0x63, 0x1c, 0x64, 0x52, 0x64, 0x56, 0x65, 0x74,
+ 0x66, 0x17, 0x67, 0x1b, 0x67, 0x56, 0x67, 0x79,
+ 0x6b, 0xba, 0x6b, 0x41, 0x6d, 0xdb, 0x6e, 0xcb,
+ 0x6e, 0x22, 0x6f, 0x1e, 0x70, 0x6e, 0x71, 0xa7,
+ 0x77, 0x35, 0x72, 0xaf, 0x72, 0x2a, 0x73, 0x71,
+ 0x74, 0x06, 0x75, 0x3b, 0x75, 0x1d, 0x76, 0x1f,
+ 0x76, 0xca, 0x76, 0xdb, 0x76, 0xf4, 0x76, 0x4a,
+ 0x77, 0x40, 0x77, 0xcc, 0x78, 0xb1, 0x7a, 0xc0,
+ 0x7b, 0x7b, 0x7c, 0x5b, 0x7d, 0xf4, 0x7d, 0x3e,
+ 0x7f, 0x05, 0x80, 0x52, 0x83, 0xef, 0x83, 0x79,
+ 0x87, 0x41, 0x89, 0x86, 0x89, 0x96, 0x89, 0xbf,
+ 0x8a, 0xf8, 0x8a, 0xcb, 0x8a, 0x01, 0x8b, 0xfe,
+ 0x8a, 0xed, 0x8a, 0x39, 0x8b, 0x8a, 0x8b, 0x08,
+ 0x8d, 0x38, 0x8f, 0x72, 0x90, 0x99, 0x91, 0x76,
+ 0x92, 0x7c, 0x96, 0xe3, 0x96, 0x56, 0x97, 0xdb,
+ 0x97, 0xff, 0x97, 0x0b, 0x98, 0x3b, 0x98, 0x12,
+ 0x9b, 0x9c, 0x9f, 0x4a, 0x28, 0x44, 0x28, 0xd5,
+ 0x33, 0x9d, 0x3b, 0x18, 0x40, 0x39, 0x40, 0x49,
+ 0x52, 0xd0, 0x5c, 0xd3, 0x7e, 0x43, 0x9f, 0x8e,
+ 0x9f, 0x2a, 0xa0, 0x02, 0x66, 0x66, 0x66, 0x69,
+ 0x66, 0x6c, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6c,
+ 0x7f, 0x01, 0x74, 0x73, 0x00, 0x74, 0x65, 0x05,
+ 0x0f, 0x11, 0x0f, 0x00, 0x0f, 0x06, 0x19, 0x11,
+ 0x0f, 0x08, 0xd9, 0x05, 0xb4, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0xf2, 0x05, 0xb7, 0x05, 0xd0, 0x05,
+ 0x12, 0x00, 0x03, 0x04, 0x0b, 0x0c, 0x0d, 0x18,
+ 0x1a, 0xe9, 0x05, 0xc1, 0x05, 0xe9, 0x05, 0xc2,
+ 0x05, 0x49, 0xfb, 0xc1, 0x05, 0x49, 0xfb, 0xc2,
+ 0x05, 0xd0, 0x05, 0xb7, 0x05, 0xd0, 0x05, 0xb8,
+ 0x05, 0xd0, 0x05, 0xbc, 0x05, 0xd8, 0x05, 0xbc,
+ 0x05, 0xde, 0x05, 0xbc, 0x05, 0xe0, 0x05, 0xbc,
+ 0x05, 0xe3, 0x05, 0xbc, 0x05, 0xb9, 0x05, 0x2d,
+ 0x03, 0x2e, 0x03, 0x2f, 0x03, 0x30, 0x03, 0x31,
+ 0x03, 0x1c, 0x00, 0x18, 0x06, 0x22, 0x06, 0x2b,
+ 0x06, 0xd0, 0x05, 0xdc, 0x05, 0x71, 0x06, 0x00,
+ 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x09, 0x09, 0x09,
+ 0x09, 0x0e, 0x0e, 0x0e, 0x0e, 0x08, 0x08, 0x08,
+ 0x08, 0x33, 0x33, 0x33, 0x33, 0x35, 0x35, 0x35,
+ 0x35, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12,
+ 0x12, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16,
+ 0x16, 0x1c, 0x1c, 0x1b, 0x1b, 0x1d, 0x1d, 0x17,
+ 0x17, 0x27, 0x27, 0x20, 0x20, 0x38, 0x38, 0x38,
+ 0x38, 0x3e, 0x3e, 0x3e, 0x3e, 0x42, 0x42, 0x42,
+ 0x42, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x50, 0x4d, 0x4d, 0x4d, 0x4d, 0x61, 0x61, 0x62,
+ 0x62, 0x49, 0x06, 0x64, 0x64, 0x64, 0x64, 0x7e,
+ 0x7e, 0x7d, 0x7d, 0x7f, 0x7f, 0x2e, 0x82, 0x82,
+ 0x7c, 0x7c, 0x80, 0x80, 0x87, 0x87, 0x87, 0x87,
+ 0x00, 0x00, 0x26, 0x06, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xaf, 0x00, 0xaf, 0x00, 0x22, 0x00, 0x22,
+ 0x00, 0xa1, 0x00, 0xa1, 0x00, 0xa0, 0x00, 0xa0,
+ 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xaa,
+ 0x00, 0xaa, 0x00, 0x23, 0x00, 0x23, 0x00, 0x23,
+ 0xcc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x26, 0x06,
+ 0x00, 0x06, 0x00, 0x07, 0x00, 0x1f, 0x00, 0x23,
+ 0x00, 0x24, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08,
+ 0x02, 0x1f, 0x02, 0x23, 0x02, 0x24, 0x04, 0x06,
+ 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x23,
+ 0x04, 0x24, 0x05, 0x06, 0x05, 0x1f, 0x05, 0x23,
+ 0x05, 0x24, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06,
+ 0x07, 0x1f, 0x08, 0x06, 0x08, 0x07, 0x08, 0x1f,
+ 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, 0x0d, 0x1f,
+ 0x0f, 0x07, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07,
+ 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x11, 0x1f,
+ 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, 0x14, 0x06,
+ 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, 0x1b, 0x08,
+ 0x1b, 0x1f, 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x07,
+ 0x1c, 0x1f, 0x1c, 0x23, 0x1c, 0x24, 0x1d, 0x01,
+ 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e,
+ 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x06,
+ 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x23,
+ 0x1e, 0x24, 0x1f, 0x06, 0x1f, 0x07, 0x1f, 0x08,
+ 0x1f, 0x1f, 0x1f, 0x23, 0x1f, 0x24, 0x20, 0x06,
+ 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, 0x23,
+ 0x20, 0x24, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x23,
+ 0x21, 0x24, 0x24, 0x06, 0x24, 0x07, 0x24, 0x08,
+ 0x24, 0x1f, 0x24, 0x23, 0x24, 0x24, 0x0a, 0x4a,
+ 0x0b, 0x4a, 0x23, 0x4a, 0x20, 0x00, 0x4c, 0x06,
+ 0x51, 0x06, 0x51, 0x06, 0xff, 0x00, 0x1f, 0x26,
+ 0x06, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x1f, 0x00,
+ 0x20, 0x00, 0x23, 0x00, 0x24, 0x02, 0x0b, 0x02,
+ 0x0c, 0x02, 0x1f, 0x02, 0x20, 0x02, 0x23, 0x02,
+ 0x24, 0x04, 0x0b, 0x04, 0x0c, 0x04, 0x1f, 0x26,
+ 0x06, 0x04, 0x20, 0x04, 0x23, 0x04, 0x24, 0x05,
+ 0x0b, 0x05, 0x0c, 0x05, 0x1f, 0x05, 0x20, 0x05,
+ 0x23, 0x05, 0x24, 0x1b, 0x23, 0x1b, 0x24, 0x1c,
+ 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x1e, 0x1d,
+ 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x1f, 0x1e,
+ 0x23, 0x1e, 0x24, 0x1f, 0x01, 0x1f, 0x1f, 0x20,
+ 0x0b, 0x20, 0x0c, 0x20, 0x1f, 0x20, 0x20, 0x20,
+ 0x23, 0x20, 0x24, 0x23, 0x4a, 0x24, 0x0b, 0x24,
+ 0x0c, 0x24, 0x1f, 0x24, 0x20, 0x24, 0x23, 0x24,
+ 0x24, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00,
+ 0x1f, 0x00, 0x21, 0x02, 0x06, 0x02, 0x07, 0x02,
+ 0x08, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x06, 0x04,
+ 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x21, 0x05,
+ 0x1f, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07,
+ 0x1f, 0x08, 0x06, 0x08, 0x1f, 0x0d, 0x06, 0x0d,
+ 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f,
+ 0x08, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10,
+ 0x08, 0x10, 0x1f, 0x11, 0x07, 0x12, 0x1f, 0x13,
+ 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b,
+ 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1c,
+ 0x07, 0x1c, 0x1f, 0x1d, 0x06, 0x1d, 0x07, 0x1d,
+ 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x06, 0x1e,
+ 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x21, 0x1f,
+ 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, 0x20,
+ 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20,
+ 0x21, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x4a, 0x24,
+ 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24,
+ 0x21, 0x00, 0x1f, 0x00, 0x21, 0x02, 0x1f, 0x02,
+ 0x21, 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x05,
+ 0x21, 0x0d, 0x1f, 0x0d, 0x21, 0x0e, 0x1f, 0x0e,
+ 0x21, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x1f, 0x20,
+ 0x1f, 0x20, 0x21, 0x24, 0x1f, 0x24, 0x21, 0x40,
+ 0x06, 0x4e, 0x06, 0x51, 0x06, 0x27, 0x06, 0x10,
+ 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13,
+ 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d,
+ 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05,
+ 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e,
+ 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d,
+ 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d,
+ 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x10,
+ 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13,
+ 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d,
+ 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05,
+ 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e,
+ 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d,
+ 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d,
+ 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x0d,
+ 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0c,
+ 0x20, 0x0d, 0x20, 0x10, 0x1e, 0x0c, 0x05, 0x0c,
+ 0x06, 0x0c, 0x07, 0x0d, 0x05, 0x0d, 0x06, 0x0d,
+ 0x07, 0x10, 0x1e, 0x11, 0x1e, 0x00, 0x24, 0x00,
+ 0x24, 0x2a, 0x06, 0x00, 0x02, 0x1b, 0x00, 0x03,
+ 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x1b, 0x00,
+ 0x04, 0x1b, 0x00, 0x1b, 0x02, 0x00, 0x1b, 0x03,
+ 0x00, 0x1b, 0x04, 0x02, 0x1b, 0x03, 0x02, 0x1b,
+ 0x03, 0x03, 0x1b, 0x20, 0x03, 0x1b, 0x1f, 0x09,
+ 0x03, 0x02, 0x09, 0x02, 0x03, 0x09, 0x02, 0x1f,
+ 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x03, 0x09, 0x1b,
+ 0x02, 0x09, 0x1b, 0x1b, 0x09, 0x1b, 0x1b, 0x0b,
+ 0x03, 0x03, 0x0b, 0x03, 0x03, 0x0b, 0x1b, 0x1b,
+ 0x0a, 0x03, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, 0x02,
+ 0x20, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x04, 0x0a,
+ 0x1b, 0x1b, 0x0a, 0x1b, 0x1b, 0x0c, 0x03, 0x1f,
+ 0x0c, 0x04, 0x1b, 0x0c, 0x04, 0x1b, 0x0d, 0x1b,
+ 0x03, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, 0x1b, 0x0d,
+ 0x1b, 0x20, 0x0f, 0x02, 0x1b, 0x0f, 0x1b, 0x1b,
+ 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1f, 0x10, 0x1b,
+ 0x1b, 0x10, 0x1b, 0x20, 0x10, 0x1b, 0x1f, 0x17,
+ 0x04, 0x1b, 0x17, 0x04, 0x1b, 0x18, 0x1b, 0x03,
+ 0x18, 0x1b, 0x1b, 0x1a, 0x03, 0x1b, 0x1a, 0x03,
+ 0x20, 0x1a, 0x03, 0x1f, 0x1a, 0x02, 0x02, 0x1a,
+ 0x02, 0x02, 0x1a, 0x04, 0x1b, 0x1a, 0x04, 0x1b,
+ 0x1a, 0x1b, 0x03, 0x1a, 0x1b, 0x03, 0x1b, 0x03,
+ 0x02, 0x1b, 0x03, 0x1b, 0x1b, 0x03, 0x20, 0x1b,
+ 0x02, 0x03, 0x1b, 0x02, 0x1b, 0x1b, 0x04, 0x02,
+ 0x1b, 0x04, 0x1b, 0x28, 0x06, 0x1d, 0x04, 0x06,
+ 0x1f, 0x1d, 0x04, 0x1f, 0x1d, 0x1d, 0x1e, 0x05,
+ 0x1d, 0x1e, 0x05, 0x21, 0x1e, 0x04, 0x1d, 0x1e,
+ 0x04, 0x1d, 0x1e, 0x04, 0x21, 0x1e, 0x1d, 0x22,
+ 0x1e, 0x1d, 0x21, 0x22, 0x1d, 0x1d, 0x22, 0x1d,
+ 0x1d, 0x00, 0x06, 0x22, 0x02, 0x04, 0x22, 0x02,
+ 0x04, 0x21, 0x02, 0x06, 0x22, 0x02, 0x06, 0x21,
+ 0x02, 0x1d, 0x22, 0x02, 0x1d, 0x21, 0x04, 0x1d,
+ 0x22, 0x04, 0x05, 0x21, 0x04, 0x1d, 0x21, 0x0b,
+ 0x06, 0x21, 0x0d, 0x05, 0x22, 0x0c, 0x05, 0x22,
+ 0x0e, 0x05, 0x22, 0x1c, 0x04, 0x22, 0x1c, 0x1d,
+ 0x22, 0x22, 0x05, 0x22, 0x22, 0x04, 0x22, 0x22,
+ 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1a, 0x1d, 0x22,
+ 0x1e, 0x05, 0x22, 0x1a, 0x1d, 0x05, 0x1c, 0x05,
+ 0x1d, 0x11, 0x1d, 0x22, 0x1b, 0x1d, 0x22, 0x1e,
+ 0x04, 0x05, 0x1d, 0x06, 0x22, 0x1c, 0x04, 0x1d,
+ 0x1b, 0x1d, 0x1d, 0x1c, 0x04, 0x1d, 0x1e, 0x04,
+ 0x05, 0x04, 0x05, 0x22, 0x05, 0x04, 0x22, 0x1d,
+ 0x04, 0x22, 0x19, 0x1d, 0x22, 0x00, 0x05, 0x22,
+ 0x1b, 0x1d, 0x1d, 0x11, 0x04, 0x1d, 0x0d, 0x1d,
+ 0x1d, 0x0b, 0x06, 0x22, 0x1e, 0x04, 0x22, 0x35,
+ 0x06, 0x00, 0x0f, 0x9d, 0x0d, 0x0f, 0x9d, 0x27,
+ 0x06, 0x00, 0x1d, 0x1d, 0x20, 0x00, 0x1c, 0x01,
+ 0x0a, 0x1e, 0x06, 0x1e, 0x08, 0x0e, 0x1d, 0x12,
+ 0x1e, 0x0a, 0x0c, 0x21, 0x1d, 0x12, 0x1d, 0x23,
+ 0x20, 0x21, 0x0c, 0x1d, 0x1e, 0x35, 0x06, 0x00,
+ 0x0f, 0x14, 0x27, 0x06, 0x0e, 0x1d, 0x22, 0xff,
+ 0x00, 0x1d, 0x1d, 0x20, 0xff, 0x12, 0x1d, 0x23,
+ 0x20, 0xff, 0x21, 0x0c, 0x1d, 0x1e, 0x27, 0x06,
+ 0x05, 0x1d, 0xff, 0x05, 0x1d, 0x00, 0x1d, 0x20,
+ 0x27, 0x06, 0x0a, 0xa5, 0x00, 0x1d, 0x2c, 0x00,
+ 0x01, 0x30, 0x02, 0x30, 0x3a, 0x00, 0x3b, 0x00,
+ 0x21, 0x00, 0x3f, 0x00, 0x16, 0x30, 0x17, 0x30,
+ 0x26, 0x20, 0x13, 0x20, 0x12, 0x01, 0x00, 0x5f,
+ 0x5f, 0x28, 0x29, 0x7b, 0x7d, 0x08, 0x30, 0x0c,
+ 0x0d, 0x08, 0x09, 0x02, 0x03, 0x00, 0x01, 0x04,
+ 0x05, 0x06, 0x07, 0x5b, 0x00, 0x5d, 0x00, 0x3e,
+ 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x5f,
+ 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x2c, 0x00, 0x01,
+ 0x30, 0x2e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x3a,
+ 0x00, 0x3f, 0x00, 0x21, 0x00, 0x14, 0x20, 0x28,
+ 0x00, 0x29, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x14,
+ 0x30, 0x15, 0x30, 0x23, 0x26, 0x2a, 0x2b, 0x2d,
+ 0x3c, 0x3e, 0x3d, 0x00, 0x5c, 0x24, 0x25, 0x40,
+ 0x40, 0x06, 0xff, 0x0b, 0x00, 0x0b, 0xff, 0x0c,
+ 0x20, 0x00, 0x4d, 0x06, 0x40, 0x06, 0xff, 0x0e,
+ 0x00, 0x0e, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x10,
+ 0x00, 0x10, 0xff, 0x11, 0x00, 0x11, 0xff, 0x12,
+ 0x00, 0x12, 0x21, 0x06, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05,
+ 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08,
+ 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f,
+ 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12,
+ 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14,
+ 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16,
+ 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18,
+ 0x18, 0x19, 0x19, 0x19, 0x19, 0x20, 0x20, 0x20,
+ 0x20, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22,
+ 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24,
+ 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26,
+ 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x29,
+ 0x29, 0x22, 0x06, 0x22, 0x00, 0x22, 0x00, 0x22,
+ 0x01, 0x22, 0x01, 0x22, 0x03, 0x22, 0x03, 0x22,
+ 0x05, 0x22, 0x05, 0x21, 0x00, 0x85, 0x29, 0x01,
+ 0x30, 0x01, 0x0b, 0x0c, 0x00, 0xfa, 0xf1, 0xa0,
+ 0xa2, 0xa4, 0xa6, 0xa8, 0xe2, 0xe4, 0xe6, 0xc2,
+ 0xfb, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac,
+ 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc,
+ 0xbe, 0xc0, 0xc3, 0xc5, 0xc7, 0xc9, 0xca, 0xcb,
+ 0xcc, 0xcd, 0xce, 0xd1, 0xd4, 0xd7, 0xda, 0xdd,
+ 0xde, 0xdf, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, 0xe8,
+ 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf2, 0x98, 0x99,
+ 0x31, 0x31, 0x4f, 0x31, 0x55, 0x31, 0x5b, 0x31,
+ 0x61, 0x31, 0xa2, 0x00, 0xa3, 0x00, 0xac, 0x00,
+ 0xaf, 0x00, 0xa6, 0x00, 0xa5, 0x00, 0xa9, 0x20,
+ 0x00, 0x00, 0x02, 0x25, 0x90, 0x21, 0x91, 0x21,
+ 0x92, 0x21, 0x93, 0x21, 0xa0, 0x25, 0xcb, 0x25,
+ 0xd0, 0x02, 0xd1, 0x02, 0xe6, 0x00, 0x99, 0x02,
+ 0x53, 0x02, 0x00, 0x00, 0xa3, 0x02, 0x66, 0xab,
+ 0xa5, 0x02, 0xa4, 0x02, 0x56, 0x02, 0x57, 0x02,
+ 0x91, 0x1d, 0x58, 0x02, 0x5e, 0x02, 0xa9, 0x02,
+ 0x64, 0x02, 0x62, 0x02, 0x60, 0x02, 0x9b, 0x02,
+ 0x27, 0x01, 0x9c, 0x02, 0x67, 0x02, 0x84, 0x02,
+ 0xaa, 0x02, 0xab, 0x02, 0x6c, 0x02, 0x04, 0xdf,
+ 0x8e, 0xa7, 0x6e, 0x02, 0x05, 0xdf, 0x8e, 0x02,
+ 0x06, 0xdf, 0xf8, 0x00, 0x76, 0x02, 0x77, 0x02,
+ 0x71, 0x00, 0x7a, 0x02, 0x08, 0xdf, 0x7d, 0x02,
+ 0x7e, 0x02, 0x80, 0x02, 0xa8, 0x02, 0xa6, 0x02,
+ 0x67, 0xab, 0xa7, 0x02, 0x88, 0x02, 0x71, 0x2c,
+ 0x00, 0x00, 0x8f, 0x02, 0xa1, 0x02, 0xa2, 0x02,
+ 0x98, 0x02, 0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01,
+ 0x0a, 0xdf, 0x1e, 0xdf, 0x41, 0x04, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x99, 0x10, 0xba, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, 0xba, 0x10,
+ 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, 0x05, 0x31,
+ 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, 0x11, 0x55,
+ 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, 0x57, 0x13,
+ 0x55, 0xb9, 0x14, 0xba, 0x14, 0xb9, 0x14, 0xb0,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x14, 0xbd,
+ 0x14, 0x55, 0x50, 0xb8, 0x15, 0xaf, 0x15, 0xb9,
+ 0x15, 0xaf, 0x15, 0x55, 0x35, 0x19, 0x30, 0x19,
+ 0x05, 0x57, 0xd1, 0x65, 0xd1, 0x58, 0xd1, 0x65,
+ 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, 0xd1, 0x6f,
+ 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, 0xd1, 0x71,
+ 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, 0x55, 0x55,
+ 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, 0xd1, 0x65,
+ 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, 0xd1, 0x6e,
+ 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, 0xd1, 0x6f,
+ 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, 0x61, 0x00,
+ 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, 0x00, 0x00,
+ 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, 0x00, 0x4e,
+ 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64,
+ 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, 0x46,
+ 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, 0x00, 0x41,
+ 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, 0x00, 0x49,
+ 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, 0x53, 0x00,
+ 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, 0x91, 0x03,
+ 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00,
+ 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03,
+ 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04,
+ 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03,
+ 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05,
+ 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03,
+ 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03,
+ 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00,
+ 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, 0x30, 0x00,
+ 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00,
+ 0x27, 0x06, 0x00, 0x01, 0x05, 0x08, 0x2a, 0x06,
+ 0x1e, 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a,
+ 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x44,
+ 0x90, 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, 0x00,
+ 0x00, 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, 0x11,
+ 0x12, 0x13, 0x00, 0x06, 0x0e, 0x02, 0x0f, 0x34,
+ 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00,
+ 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x2d,
+ 0x06, 0x00, 0x00, 0x4a, 0x06, 0x00, 0x00, 0x44,
+ 0x06, 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, 0x39,
+ 0x06, 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, 0x00,
+ 0x00, 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2e,
+ 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a,
+ 0x06, 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, 0x6f,
+ 0x06, 0x00, 0x00, 0x28, 0x06, 0x2c, 0x06, 0x00,
+ 0x00, 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2d,
+ 0x06, 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, 0x00,
+ 0x00, 0x45, 0x06, 0x46, 0x06, 0x33, 0x06, 0x39,
+ 0x06, 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, 0x00,
+ 0x00, 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e,
+ 0x06, 0x00, 0x00, 0x36, 0x06, 0x38, 0x06, 0x3a,
+ 0x06, 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, 0x27,
+ 0x06, 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, 0x0b,
+ 0x06, 0x10, 0x23, 0x2a, 0x06, 0x1a, 0x1b, 0x1c,
+ 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00,
+ 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, 0x06,
+ 0x2c, 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, 0x06,
+ 0x32, 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06,
+ 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17,
+ 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06,
+ 0x0c, 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, 0x2c,
+ 0x00, 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, 0x14,
+ 0x30, 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, 0x43,
+ 0x44, 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, 0x4d,
+ 0x56, 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, 0x56,
+ 0x57, 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, 0x52,
+ 0x44, 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, 0x68,
+ 0x4b, 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, 0x30,
+ 0x8c, 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, 0x59,
+ 0xa4, 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, 0x65,
+ 0x4d, 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, 0x65,
+ 0x1d, 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, 0x8c,
+ 0xf0, 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, 0x62,
+ 0x55, 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, 0x90,
+ 0xe6, 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, 0x63,
+ 0x70, 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, 0x7a,
+ 0x08, 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, 0x67,
+ 0x33, 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, 0x91,
+ 0x14, 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, 0x4e,
+ 0x8c, 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, 0x62,
+ 0xd7, 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, 0x5f,
+ 0xef, 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, 0x00,
+ 0x09, 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, 0xbb,
+ 0x4f, 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, 0xe7,
+ 0x50, 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, 0x4d,
+ 0x51, 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, 0x1c,
+ 0x05, 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, 0x4b,
+ 0x05, 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, 0xac,
+ 0x51, 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, 0x03,
+ 0x52, 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, 0x72,
+ 0x52, 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, 0x20,
+ 0x80, 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, 0x52,
+ 0x00, 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, 0x82,
+ 0x8a, 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, 0x2c,
+ 0x0a, 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, 0x63,
+ 0x0b, 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, 0x9e,
+ 0x54, 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, 0xa2,
+ 0x54, 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, 0x63,
+ 0x55, 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, 0xab,
+ 0x55, 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, 0x06,
+ 0x56, 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, 0x07,
+ 0x52, 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, 0x0d,
+ 0x58, 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, 0xac,
+ 0x58, 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, 0x06,
+ 0x59, 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, 0xa8,
+ 0x16, 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, 0x27,
+ 0x5a, 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, 0xfc,
+ 0x36, 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, 0xc8,
+ 0x19, 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, 0xf3,
+ 0x5b, 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, 0x53,
+ 0x5f, 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, 0x6e,
+ 0x5c, 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, 0x43,
+ 0x5d, 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, 0x7c,
+ 0x5d, 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, 0xfd,
+ 0x5d, 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, 0x62,
+ 0x38, 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, 0xb3,
+ 0x5e, 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, 0xfe,
+ 0x5e, 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, 0x22,
+ 0x5f, 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, 0xda,
+ 0x61, 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, 0x9a,
+ 0x5f, 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, 0x81,
+ 0x60, 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, 0xd4,
+ 0x26, 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00,
+ 0x00, 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00,
+ 0x08, 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, 0x02,
+ 0x48, 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, 0x46,
+ 0x6a, 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, 0xd3,
+ 0x5d, 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, 0x2b,
+ 0x3d, 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, 0x63,
+ 0xe4, 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, 0x63,
+ 0xa9, 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, 0x64,
+ 0x9d, 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, 0x65,
+ 0x6c, 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, 0x66,
+ 0x49, 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, 0x3b,
+ 0xe4, 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, 0x67,
+ 0x9c, 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, 0x67,
+ 0x1b, 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, 0x67,
+ 0xc3, 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, 0x67,
+ 0x52, 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, 0x68,
+ 0x1f, 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, 0x69,
+ 0xa3, 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, 0x36,
+ 0xdb, 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, 0x38,
+ 0x54, 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, 0x6b,
+ 0xba, 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, 0x1d,
+ 0xfa, 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, 0x6c,
+ 0xcd, 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, 0x6d,
+ 0x77, 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, 0x6d,
+ 0x85, 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, 0x6e,
+ 0x6e, 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, 0x6e,
+ 0xd1, 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, 0x3f,
+ 0x8e, 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, 0x70,
+ 0x1b, 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, 0x70,
+ 0x77, 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, 0x71,
+ 0x63, 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, 0x72,
+ 0x35, 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, 0x72,
+ 0x95, 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00,
+ 0x00, 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, 0x20,
+ 0x00, 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, 0x20,
+ 0x14, 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, 0x3e,
+ 0xa5, 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, 0x74,
+ 0x5c, 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, 0x74,
+ 0x1b, 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, 0x75,
+ 0x92, 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, 0x76,
+ 0xa1, 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, 0x3f,
+ 0x08, 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, 0x50,
+ 0x19, 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, 0x77,
+ 0x1f, 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, 0x77,
+ 0x46, 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, 0x78,
+ 0x8c, 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, 0x56,
+ 0x56, 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, 0x79,
+ 0xeb, 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, 0x7a,
+ 0x4f, 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, 0x5a,
+ 0xee, 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, 0x7b,
+ 0xc9, 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, 0x7c,
+ 0xa0, 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, 0x7d,
+ 0x86, 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, 0x7d,
+ 0x02, 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, 0x62,
+ 0x47, 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, 0x7f,
+ 0x3e, 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, 0x80,
+ 0xda, 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, 0x65,
+ 0x70, 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, 0x80,
+ 0x03, 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, 0x5a,
+ 0xa7, 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, 0x33,
+ 0x01, 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, 0x44,
+ 0x91, 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, 0x52,
+ 0xb1, 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, 0x82,
+ 0x3c, 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, 0x83,
+ 0xad, 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, 0x83,
+ 0x57, 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, 0x83,
+ 0xdc, 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, 0x00,
+ 0x00, 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, 0x20,
+ 0x80, 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, 0x02,
+ 0x80, 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, 0x6c,
+ 0x2b, 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, 0x85,
+ 0xca, 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, 0x45,
+ 0x61, 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, 0x45,
+ 0x50, 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, 0x86,
+ 0xa9, 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, 0x86,
+ 0x79, 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, 0x87,
+ 0xd7, 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, 0x45,
+ 0x60, 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, 0x88,
+ 0xde, 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, 0x34,
+ 0xae, 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, 0x46,
+ 0xa0, 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, 0x8c,
+ 0xa8, 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, 0x8d,
+ 0x77, 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, 0x8d,
+ 0xbc, 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, 0x8e,
+ 0x38, 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, 0x90,
+ 0xf1, 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, 0x91,
+ 0x38, 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, 0x92,
+ 0xf9, 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, 0x95,
+ 0x95, 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, 0x49,
+ 0xc3, 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, 0x91,
+ 0x1a, 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, 0x97,
+ 0x0a, 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, 0x98,
+ 0x0b, 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, 0x98,
+ 0x33, 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, 0x99,
+ 0xfe, 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, 0x9b,
+ 0x40, 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, 0x4c,
+ 0x67, 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, 0xa1,
+ 0x0e, 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, 0x4d,
+ 0xf9, 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, 0x9f,
+ 0x16, 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, 0x88,
+ 0xa0, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x28,
+ 0x00, 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, 0x80,
+ 0x80, 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, 0x00,
+ 0x20, 0x2a, 0x00, 0x80,
+};
+
+static const uint16_t unicode_comp_table[945] = {
+ 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0,
+ 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982,
+ 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8,
+ 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292,
+ 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304,
+ 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306,
+ 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e,
+ 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8,
+ 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380,
+ 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac,
+ 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444,
+ 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940,
+ 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce,
+ 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296,
+ 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844,
+ 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e,
+ 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998,
+ 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322,
+ 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac,
+ 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326,
+ 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc,
+ 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce,
+ 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354,
+ 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3,
+ 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981,
+ 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7,
+ 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291,
+ 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303,
+ 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03,
+ 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d,
+ 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5,
+ 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343,
+ 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347,
+ 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5,
+ 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7,
+ 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3,
+ 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543,
+ 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1,
+ 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991,
+ 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d,
+ 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b,
+ 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3,
+ 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997,
+ 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343,
+ 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f,
+ 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf,
+ 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357,
+ 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448,
+ 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a,
+ 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06,
+ 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447,
+ 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289,
+ 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d,
+ 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb,
+ 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f,
+ 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306,
+ 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d,
+ 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8,
+ 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4,
+ 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882,
+ 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b,
+ 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541,
+ 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8,
+ 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8,
+ 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06,
+ 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850,
+ 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0,
+ 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940,
+ 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05,
+ 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0,
+ 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81,
+ 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184,
+ 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182,
+ 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0,
+ 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242,
+ 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0,
+ 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141,
+ 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245,
+ 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080,
+ 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341,
+ 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480,
+ 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800,
+ 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901,
+ 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80,
+ 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008,
+ 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9,
+ 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457,
+ 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0,
+ 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5,
+ 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583,
+ 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf,
+ 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683,
+ 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01,
+ 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc,
+ 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b,
+ 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3,
+ 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df,
+ 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783,
+ 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844,
+ 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e,
+ 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851,
+ 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a,
+ 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180,
+ 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001,
+ 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700,
+ 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982,
+ 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81,
+ 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41,
+ 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01,
+ 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448,
+ 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480,
+ 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541,
+ 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702,
+ 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712,
+ 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0,
+ 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900,
+ 0x5901, 0x5902, 0x5903, 0x5940, 0x8f40, 0x8f42, 0x8f80, 0x8fc0,
+ 0x8fc1, 0x9000, 0x9001, 0x9041, 0x9040, 0x9043, 0x9080, 0x9081,
+ 0x90c0,
+};
+
+typedef enum {
+ UNICODE_GC_Cn,
+ UNICODE_GC_Lu,
+ UNICODE_GC_Ll,
+ UNICODE_GC_Lt,
+ UNICODE_GC_Lm,
+ UNICODE_GC_Lo,
+ UNICODE_GC_Mn,
+ UNICODE_GC_Mc,
+ UNICODE_GC_Me,
+ UNICODE_GC_Nd,
+ UNICODE_GC_Nl,
+ UNICODE_GC_No,
+ UNICODE_GC_Sm,
+ UNICODE_GC_Sc,
+ UNICODE_GC_Sk,
+ UNICODE_GC_So,
+ UNICODE_GC_Pc,
+ UNICODE_GC_Pd,
+ UNICODE_GC_Ps,
+ UNICODE_GC_Pe,
+ UNICODE_GC_Pi,
+ UNICODE_GC_Pf,
+ UNICODE_GC_Po,
+ UNICODE_GC_Zs,
+ UNICODE_GC_Zl,
+ UNICODE_GC_Zp,
+ UNICODE_GC_Cc,
+ UNICODE_GC_Cf,
+ UNICODE_GC_Cs,
+ UNICODE_GC_Co,
+ UNICODE_GC_LC,
+ UNICODE_GC_L,
+ UNICODE_GC_M,
+ UNICODE_GC_N,
+ UNICODE_GC_S,
+ UNICODE_GC_P,
+ UNICODE_GC_Z,
+ UNICODE_GC_C,
+ UNICODE_GC_COUNT,
+} UnicodeGCEnum;
+
+static const char unicode_gc_name_table[] =
+ "Cn,Unassigned" "\0"
+ "Lu,Uppercase_Letter" "\0"
+ "Ll,Lowercase_Letter" "\0"
+ "Lt,Titlecase_Letter" "\0"
+ "Lm,Modifier_Letter" "\0"
+ "Lo,Other_Letter" "\0"
+ "Mn,Nonspacing_Mark" "\0"
+ "Mc,Spacing_Mark" "\0"
+ "Me,Enclosing_Mark" "\0"
+ "Nd,Decimal_Number,digit" "\0"
+ "Nl,Letter_Number" "\0"
+ "No,Other_Number" "\0"
+ "Sm,Math_Symbol" "\0"
+ "Sc,Currency_Symbol" "\0"
+ "Sk,Modifier_Symbol" "\0"
+ "So,Other_Symbol" "\0"
+ "Pc,Connector_Punctuation" "\0"
+ "Pd,Dash_Punctuation" "\0"
+ "Ps,Open_Punctuation" "\0"
+ "Pe,Close_Punctuation" "\0"
+ "Pi,Initial_Punctuation" "\0"
+ "Pf,Final_Punctuation" "\0"
+ "Po,Other_Punctuation" "\0"
+ "Zs,Space_Separator" "\0"
+ "Zl,Line_Separator" "\0"
+ "Zp,Paragraph_Separator" "\0"
+ "Cc,Control,cntrl" "\0"
+ "Cf,Format" "\0"
+ "Cs,Surrogate" "\0"
+ "Co,Private_Use" "\0"
+ "LC,Cased_Letter" "\0"
+ "L,Letter" "\0"
+ "M,Mark,Combining_Mark" "\0"
+ "N,Number" "\0"
+ "S,Symbol" "\0"
+ "P,Punctuation,punct" "\0"
+ "Z,Separator" "\0"
+ "C,Other" "\0"
+;
+
+static const uint8_t unicode_gc_table[3897] = {
+ 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13,
+ 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36,
+ 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e,
+ 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c,
+ 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e,
+ 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f,
+ 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05,
+ 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2,
+ 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff,
+ 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02,
+ 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02,
+ 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f,
+ 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02,
+ 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01,
+ 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02,
+ 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21,
+ 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22,
+ 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05,
+ 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee,
+ 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09,
+ 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04,
+ 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41,
+ 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00,
+ 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42,
+ 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02,
+ 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86,
+ 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58,
+ 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21,
+ 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25,
+ 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06,
+ 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0,
+ 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6,
+ 0x03, 0x16, 0x1b, 0x56, 0xe5, 0x18, 0x04, 0xe5,
+ 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, 0x06,
+ 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, 0xa6,
+ 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, 0x45,
+ 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, 0x06,
+ 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, 0xe6,
+ 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, 0x19,
+ 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, 0x06,
+ 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, 0x04,
+ 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, 0xe5,
+ 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, 0x80,
+ 0xe5, 0x10, 0x0e, 0xa5, 0x00, 0x3b, 0xa0, 0xe6,
+ 0x00, 0xe5, 0x21, 0x04, 0xe6, 0x10, 0x1b, 0xe6,
+ 0x18, 0x07, 0xe5, 0x2e, 0x06, 0x07, 0x06, 0x05,
+ 0x47, 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, 0xc6,
+ 0xe5, 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04,
+ 0xe5, 0x07, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20,
+ 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x05,
+ 0x40, 0x65, 0x20, 0x06, 0x05, 0x47, 0x66, 0x20,
+ 0x27, 0x20, 0x27, 0x06, 0x05, 0xe0, 0x00, 0x07,
+ 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02,
+ 0x25, 0x2d, 0xab, 0x0f, 0x0d, 0x05, 0x16, 0x06,
+ 0x20, 0x26, 0x07, 0x00, 0xa5, 0x60, 0x25, 0x20,
+ 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x25,
+ 0x00, 0x25, 0x20, 0x06, 0x00, 0x47, 0x26, 0x60,
+ 0x26, 0x20, 0x46, 0x40, 0x06, 0xc0, 0x65, 0x00,
+ 0x05, 0xc0, 0xe9, 0x02, 0x26, 0x45, 0x06, 0x16,
+ 0xe0, 0x02, 0x26, 0x07, 0x00, 0xe5, 0x01, 0x00,
+ 0x45, 0x00, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25,
+ 0x00, 0x85, 0x20, 0x06, 0x05, 0x47, 0x86, 0x00,
+ 0x26, 0x07, 0x00, 0x27, 0x06, 0x20, 0x05, 0xe0,
+ 0x07, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x16, 0x0d,
+ 0xc0, 0x05, 0xa6, 0x00, 0x06, 0x27, 0x00, 0xe5,
+ 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5,
+ 0x00, 0x25, 0x00, 0x85, 0x20, 0x06, 0x05, 0x07,
+ 0x06, 0x07, 0x66, 0x20, 0x27, 0x20, 0x27, 0x06,
+ 0xc0, 0x26, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26,
+ 0x20, 0xe9, 0x02, 0x0f, 0x05, 0xab, 0xe0, 0x02,
+ 0x06, 0x05, 0x00, 0xa5, 0x40, 0x45, 0x00, 0x65,
+ 0x40, 0x25, 0x00, 0x05, 0x00, 0x25, 0x40, 0x25,
+ 0x40, 0x45, 0x40, 0xe5, 0x04, 0x60, 0x27, 0x06,
+ 0x27, 0x40, 0x47, 0x00, 0x47, 0x06, 0x20, 0x05,
+ 0xa0, 0x07, 0xe0, 0x06, 0xe9, 0x02, 0x4b, 0xaf,
+ 0x0d, 0x0f, 0x80, 0x06, 0x47, 0x06, 0xe5, 0x00,
+ 0x00, 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x08,
+ 0x20, 0x06, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00,
+ 0x66, 0xc0, 0x26, 0x00, 0x45, 0x20, 0x05, 0x20,
+ 0x25, 0x26, 0x20, 0xe9, 0x02, 0xc0, 0x16, 0xcb,
+ 0x0f, 0x05, 0x06, 0x27, 0x16, 0xe5, 0x00, 0x00,
+ 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x02, 0x00,
+ 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x87, 0x00,
+ 0x06, 0x27, 0x00, 0x27, 0x26, 0xc0, 0x27, 0xa0,
+ 0x25, 0x00, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x00,
+ 0x25, 0xe0, 0x05, 0x26, 0x27, 0xe5, 0x01, 0x00,
+ 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, 0x47, 0x66,
+ 0x00, 0x47, 0x00, 0x47, 0x06, 0x05, 0x0f, 0x60,
+ 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, 0xe9, 0x02,
+ 0xeb, 0x01, 0x0f, 0xa5, 0x00, 0x06, 0x27, 0x00,
+ 0xe5, 0x0a, 0x40, 0xe5, 0x10, 0x00, 0xe5, 0x01,
+ 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, 0x60, 0x47,
+ 0x46, 0x00, 0x06, 0x00, 0xe7, 0x00, 0xa0, 0xe9,
+ 0x02, 0x20, 0x27, 0x16, 0xe0, 0x04, 0xe5, 0x28,
+ 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, 0x04, 0xe6,
+ 0x00, 0x16, 0xe9, 0x02, 0x36, 0xe0, 0x1d, 0x25,
+ 0x00, 0x05, 0x00, 0x85, 0x00, 0xe5, 0x10, 0x00,
+ 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, 0xe6, 0x01,
+ 0x05, 0x20, 0x85, 0x00, 0x04, 0x00, 0xa6, 0x20,
+ 0xe9, 0x02, 0x20, 0x65, 0xe0, 0x18, 0x05, 0x4f,
+ 0xf6, 0x07, 0x0f, 0x16, 0x4f, 0x26, 0xaf, 0xe9,
+ 0x02, 0xeb, 0x02, 0x0f, 0x06, 0x0f, 0x06, 0x0f,
+ 0x06, 0x12, 0x13, 0x12, 0x13, 0x27, 0xe5, 0x00,
+ 0x00, 0xe5, 0x1c, 0x60, 0xe6, 0x06, 0x07, 0x86,
+ 0x16, 0x26, 0x85, 0xe6, 0x03, 0x00, 0xe6, 0x1c,
+ 0x00, 0xef, 0x00, 0x06, 0xaf, 0x00, 0x2f, 0x96,
+ 0x6f, 0x36, 0xe0, 0x1d, 0xe5, 0x23, 0x27, 0x66,
+ 0x07, 0xa6, 0x07, 0x26, 0x27, 0x26, 0x05, 0xe9,
+ 0x02, 0xb6, 0xa5, 0x27, 0x26, 0x65, 0x46, 0x05,
+ 0x47, 0x25, 0xc7, 0x45, 0x66, 0xe5, 0x05, 0x06,
+ 0x27, 0x26, 0xa7, 0x06, 0x05, 0x07, 0xe9, 0x02,
+ 0x47, 0x06, 0x2f, 0xe1, 0x1e, 0x00, 0x01, 0x80,
+ 0x01, 0x20, 0xe2, 0x23, 0x16, 0x04, 0x42, 0xe5,
+ 0x80, 0xc1, 0x00, 0x65, 0x20, 0xc5, 0x00, 0x05,
+ 0x00, 0x65, 0x20, 0xe5, 0x21, 0x00, 0x65, 0x20,
+ 0xe5, 0x19, 0x00, 0x65, 0x20, 0xc5, 0x00, 0x05,
+ 0x00, 0x65, 0x20, 0xe5, 0x07, 0x00, 0xe5, 0x31,
+ 0x00, 0x65, 0x20, 0xe5, 0x3b, 0x20, 0x46, 0xf6,
+ 0x01, 0xeb, 0x0c, 0x40, 0xe5, 0x08, 0xef, 0x02,
+ 0xa0, 0xe1, 0x4e, 0x20, 0xa2, 0x20, 0x11, 0xe5,
+ 0x81, 0xe4, 0x0f, 0x16, 0xe5, 0x09, 0x17, 0xe5,
+ 0x12, 0x12, 0x13, 0x40, 0xe5, 0x43, 0x56, 0x4a,
+ 0xe5, 0x00, 0xc0, 0xe5, 0x0a, 0x46, 0x07, 0xe0,
+ 0x01, 0xe5, 0x0b, 0x26, 0x07, 0x36, 0xe0, 0x01,
+ 0xe5, 0x0a, 0x26, 0xe0, 0x04, 0xe5, 0x05, 0x00,
+ 0x45, 0x00, 0x26, 0xe0, 0x04, 0xe5, 0x2c, 0x26,
+ 0x07, 0xc6, 0xe7, 0x00, 0x06, 0x27, 0xe6, 0x03,
+ 0x56, 0x04, 0x56, 0x0d, 0x05, 0x06, 0x20, 0xe9,
+ 0x02, 0xa0, 0xeb, 0x02, 0xa0, 0xb6, 0x11, 0x76,
+ 0x46, 0x1b, 0x06, 0xe9, 0x02, 0xa0, 0xe5, 0x1b,
+ 0x04, 0xe5, 0x2d, 0xc0, 0x85, 0x26, 0xe5, 0x1a,
+ 0x06, 0x05, 0x80, 0xe5, 0x3e, 0xe0, 0x02, 0xe5,
+ 0x17, 0x00, 0x46, 0x67, 0x26, 0x47, 0x60, 0x27,
+ 0x06, 0xa7, 0x46, 0x60, 0x0f, 0x40, 0x36, 0xe9,
+ 0x02, 0xe5, 0x16, 0x20, 0x85, 0xe0, 0x03, 0xe5,
+ 0x24, 0x60, 0xe5, 0x12, 0xa0, 0xe9, 0x02, 0x0b,
+ 0x40, 0xef, 0x1a, 0xe5, 0x0f, 0x26, 0x27, 0x06,
+ 0x20, 0x36, 0xe5, 0x2d, 0x07, 0x06, 0x07, 0xc6,
+ 0x00, 0x06, 0x07, 0x06, 0x27, 0xe6, 0x00, 0xa7,
+ 0xe6, 0x02, 0x20, 0x06, 0xe9, 0x02, 0xa0, 0xe9,
+ 0x02, 0xa0, 0xd6, 0x04, 0xb6, 0x20, 0xe6, 0x06,
+ 0x08, 0xe6, 0x08, 0xe0, 0x29, 0x66, 0x07, 0xe5,
+ 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, 0x06,
+ 0x27, 0xe5, 0x00, 0x40, 0xe9, 0x02, 0xd6, 0xef,
+ 0x02, 0xe6, 0x01, 0xef, 0x01, 0x36, 0x00, 0x26,
+ 0x07, 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, 0x07,
+ 0x46, 0x25, 0xe9, 0x02, 0xe5, 0x24, 0x06, 0x07,
+ 0x26, 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, 0x00,
+ 0x76, 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, 0x27,
+ 0x26, 0x40, 0x96, 0xe9, 0x02, 0x40, 0x45, 0xe9,
+ 0x02, 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, 0xc0,
+ 0xe1, 0x23, 0x20, 0x41, 0xf6, 0x00, 0xe0, 0x00,
+ 0x46, 0x16, 0xe6, 0x05, 0x07, 0xc6, 0x65, 0x06,
+ 0xa5, 0x06, 0x25, 0x07, 0x26, 0x05, 0x80, 0xe2,
+ 0x24, 0xe4, 0x37, 0xe2, 0x05, 0x04, 0xe2, 0x1a,
+ 0xe4, 0x1d, 0xe6, 0x38, 0xff, 0x80, 0x0e, 0xe2,
+ 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, 0x00, 0xa2,
+ 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, 0x00, 0xe2,
+ 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, 0x20, 0xe2,
+ 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, 0x20, 0xe2,
+ 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, 0x00, 0xe2,
+ 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, 0x61, 0x03,
+ 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, 0x61, 0x03,
+ 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, 0x4e, 0xe2,
+ 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, 0x22, 0x61,
+ 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, 0xb1, 0x36,
+ 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, 0x14, 0xf6,
+ 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, 0x01, 0x14,
+ 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, 0x13, 0xf6,
+ 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, 0x17, 0x9b,
+ 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, 0xab, 0x4c,
+ 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, 0x12, 0x13,
+ 0x00, 0xe4, 0x05, 0x40, 0xed, 0x19, 0xe0, 0x07,
+ 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, 0x04, 0xe0,
+ 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, 0x02, 0x41,
+ 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, 0x0c, 0x81,
+ 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, 0x0f, 0x61,
+ 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, 0x2f, 0x22,
+ 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, 0x2f, 0x02,
+ 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, 0x6a, 0x0b,
+ 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, 0x0c, 0x2f,
+ 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, 0x17, 0x2c,
+ 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, 0xec, 0x80,
+ 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, 0x13, 0xef,
+ 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, 0x49, 0x0c,
+ 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, 0xac, 0xef,
+ 0x3d, 0xe0, 0x11, 0xef, 0x03, 0xe0, 0x0d, 0xeb,
+ 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, 0x80, 0x2f,
+ 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, 0xec, 0x00,
+ 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, 0xef, 0x24,
+ 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, 0x13, 0x12,
+ 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xec,
+ 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, 0x12, 0x13,
+ 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, 0xec, 0x80,
+ 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, 0xac, 0xef,
+ 0x1f, 0x20, 0xef, 0x18, 0x00, 0xef, 0x61, 0xe1,
+ 0x28, 0xe2, 0x28, 0x5f, 0x21, 0x22, 0xdf, 0x41,
+ 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, 0x41, 0x02,
+ 0xff, 0x5a, 0x02, 0xaf, 0x7f, 0x46, 0x3f, 0x80,
+ 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, 0x02, 0x80,
+ 0x02, 0x20, 0xe5, 0x30, 0xc0, 0x04, 0x16, 0xe0,
+ 0x06, 0x06, 0xe5, 0x0f, 0xe0, 0x01, 0xc5, 0x00,
+ 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00,
+ 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xe6, 0x18,
+ 0x36, 0x14, 0x15, 0x14, 0x15, 0x56, 0x14, 0x15,
+ 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, 0x36, 0x11,
+ 0x16, 0x14, 0x15, 0x36, 0x14, 0x15, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x96, 0x04,
+ 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, 0x12, 0xf6,
+ 0x05, 0x2f, 0x56, 0x12, 0x13, 0x12, 0x13, 0x12,
+ 0x13, 0x12, 0x13, 0x11, 0xe0, 0x1a, 0xef, 0x12,
+ 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, 0x80, 0x4e,
+ 0xe0, 0x12, 0xef, 0x04, 0x60, 0x17, 0x56, 0x0f,
+ 0x04, 0x05, 0x0a, 0x12, 0x13, 0x12, 0x13, 0x12,
+ 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, 0x12,
+ 0x33, 0x0f, 0xea, 0x01, 0x66, 0x27, 0x11, 0x84,
+ 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, 0x00, 0xe5,
+ 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, 0x11, 0xe5,
+ 0x52, 0x16, 0x44, 0x05, 0x80, 0xe5, 0x23, 0x00,
+ 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, 0x02, 0xe5,
+ 0x18, 0xef, 0x1c, 0xe0, 0x04, 0xe5, 0x08, 0xef,
+ 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, 0xeb, 0x00,
+ 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, 0x02, 0xef,
+ 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, 0xe5, 0x99,
+ 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, 0x8d, 0x04,
+ 0xe5, 0x83, 0xef, 0x40, 0xef, 0x2f, 0xe0, 0x01,
+ 0xe5, 0x20, 0xa4, 0x36, 0xe5, 0x80, 0x84, 0x04,
+ 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, 0xe0, 0x0c,
+ 0xff, 0x26, 0x05, 0x06, 0x48, 0x16, 0xe6, 0x02,
+ 0x16, 0x04, 0xff, 0x14, 0x24, 0x26, 0xe5, 0x3e,
+ 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, 0xee, 0x0f,
+ 0xe4, 0x01, 0x2e, 0xff, 0x06, 0x22, 0xff, 0x36,
+ 0x04, 0xe2, 0x00, 0x9f, 0xff, 0x02, 0x04, 0x2e,
+ 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, 0x61, 0x02,
+ 0x81, 0x02, 0xff, 0x07, 0x41, 0x02, 0x3f, 0x80,
+ 0x3f, 0x00, 0x02, 0x00, 0x02, 0x7f, 0xe0, 0x10,
+ 0x44, 0x3f, 0x05, 0x24, 0x02, 0xc5, 0x06, 0x45,
+ 0x06, 0x65, 0x06, 0xe5, 0x0f, 0x27, 0x26, 0x07,
+ 0x6f, 0x06, 0x40, 0xab, 0x2f, 0x0d, 0x0f, 0xa0,
+ 0xe5, 0x2c, 0x76, 0xe0, 0x00, 0x27, 0xe5, 0x2a,
+ 0xe7, 0x08, 0x26, 0xe0, 0x00, 0x36, 0xe9, 0x02,
+ 0xa0, 0xe6, 0x0a, 0xa5, 0x56, 0x05, 0x16, 0x25,
+ 0x06, 0xe9, 0x02, 0xe5, 0x14, 0xe6, 0x00, 0x36,
+ 0xe5, 0x0f, 0xe6, 0x03, 0x27, 0xe0, 0x03, 0x16,
+ 0xe5, 0x15, 0x40, 0x46, 0x07, 0xe5, 0x27, 0x06,
+ 0x27, 0x66, 0x27, 0x26, 0x47, 0xf6, 0x05, 0x00,
+ 0x04, 0xe9, 0x02, 0x60, 0x36, 0x85, 0x06, 0x04,
+ 0xe5, 0x01, 0xe9, 0x02, 0x85, 0x00, 0xe5, 0x21,
+ 0xa6, 0x27, 0x26, 0x27, 0x26, 0xe0, 0x01, 0x45,
+ 0x06, 0xe5, 0x00, 0x06, 0x07, 0x20, 0xe9, 0x02,
+ 0x20, 0x76, 0xe5, 0x08, 0x04, 0xa5, 0x4f, 0x05,
+ 0x07, 0x06, 0x07, 0xe5, 0x2a, 0x06, 0x05, 0x46,
+ 0x25, 0x26, 0x85, 0x26, 0x05, 0x06, 0x05, 0xe0,
+ 0x10, 0x25, 0x04, 0x36, 0xe5, 0x03, 0x07, 0x26,
+ 0x27, 0x36, 0x05, 0x24, 0x07, 0x06, 0xe0, 0x02,
+ 0xa5, 0x20, 0xa5, 0x20, 0xa5, 0xe0, 0x01, 0xc5,
+ 0x00, 0xc5, 0x00, 0xe2, 0x23, 0x0e, 0x64, 0xe2,
+ 0x01, 0x04, 0x2e, 0x60, 0xe2, 0x48, 0xe5, 0x1b,
+ 0x27, 0x06, 0x27, 0x06, 0x27, 0x16, 0x07, 0x06,
+ 0x20, 0xe9, 0x02, 0xa0, 0xe5, 0xab, 0x1c, 0xe0,
+ 0x04, 0xe5, 0x0f, 0x60, 0xe5, 0x29, 0x60, 0xfc,
+ 0x87, 0x78, 0xfd, 0x98, 0x78, 0xe5, 0x80, 0xe6,
+ 0x20, 0xe5, 0x62, 0xe0, 0x1e, 0xc2, 0xe0, 0x04,
+ 0x82, 0x80, 0x05, 0x06, 0xe5, 0x02, 0x0c, 0xe5,
+ 0x05, 0x00, 0x85, 0x00, 0x05, 0x00, 0x25, 0x00,
+ 0x25, 0x00, 0xe5, 0x64, 0xee, 0x09, 0xe0, 0x08,
+ 0xe5, 0x80, 0xe3, 0x13, 0x12, 0xef, 0x08, 0xe5,
+ 0x38, 0x20, 0xe5, 0x2e, 0xc0, 0x0f, 0xe0, 0x18,
+ 0xe5, 0x04, 0x0d, 0x4f, 0xe6, 0x08, 0xd6, 0x12,
+ 0x13, 0x16, 0xa0, 0xe6, 0x08, 0x16, 0x31, 0x30,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x36, 0x12, 0x13, 0x76, 0x50, 0x56, 0x00, 0x76,
+ 0x11, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x56,
+ 0x0c, 0x11, 0x4c, 0x00, 0x16, 0x0d, 0x36, 0x60,
+ 0x85, 0x00, 0xe5, 0x7f, 0x20, 0x1b, 0x00, 0x56,
+ 0x0d, 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, 0x11,
+ 0x36, 0xe9, 0x02, 0x36, 0x4c, 0x36, 0xe1, 0x12,
+ 0x12, 0x16, 0x13, 0x0e, 0x10, 0x0e, 0xe2, 0x12,
+ 0x12, 0x0c, 0x13, 0x0c, 0x12, 0x13, 0x16, 0x12,
+ 0x13, 0x36, 0xe5, 0x02, 0x04, 0xe5, 0x25, 0x24,
+ 0xe5, 0x17, 0x40, 0xa5, 0x20, 0xa5, 0x20, 0xa5,
+ 0x20, 0x45, 0x40, 0x2d, 0x0c, 0x0e, 0x0f, 0x2d,
+ 0x00, 0x0f, 0x6c, 0x2f, 0xe0, 0x02, 0x5b, 0x2f,
+ 0x20, 0xe5, 0x04, 0x00, 0xe5, 0x12, 0x00, 0xe5,
+ 0x0b, 0x00, 0x25, 0x00, 0xe5, 0x07, 0x20, 0xe5,
+ 0x06, 0xe0, 0x1a, 0xe5, 0x73, 0x80, 0x56, 0x60,
+ 0xeb, 0x25, 0x40, 0xef, 0x01, 0xea, 0x2d, 0x6b,
+ 0xef, 0x09, 0x2b, 0x4f, 0x00, 0xef, 0x05, 0x40,
+ 0x0f, 0xe0, 0x27, 0xef, 0x25, 0x06, 0xe0, 0x7a,
+ 0xe5, 0x15, 0x40, 0xe5, 0x29, 0xe0, 0x07, 0x06,
+ 0xeb, 0x13, 0x60, 0xe5, 0x18, 0x6b, 0xe0, 0x01,
+ 0xe5, 0x0c, 0x0a, 0xe5, 0x00, 0x0a, 0x80, 0xe5,
+ 0x1e, 0x86, 0x80, 0xe5, 0x16, 0x00, 0x16, 0xe5,
+ 0x1c, 0x60, 0xe5, 0x00, 0x16, 0x8a, 0xe0, 0x22,
+ 0xe1, 0x20, 0xe2, 0x20, 0xe5, 0x46, 0x20, 0xe9,
+ 0x02, 0xa0, 0xe1, 0x1c, 0x60, 0xe2, 0x1c, 0x60,
+ 0xe5, 0x20, 0xe0, 0x00, 0xe5, 0x2c, 0xe0, 0x03,
+ 0x16, 0xe1, 0x03, 0x00, 0xe1, 0x07, 0x00, 0xc1,
+ 0x00, 0x21, 0x00, 0xe2, 0x03, 0x00, 0xe2, 0x07,
+ 0x00, 0xc2, 0x00, 0x22, 0xe0, 0x3b, 0xe5, 0x80,
+ 0xaf, 0xe0, 0x01, 0xe5, 0x0e, 0xe0, 0x02, 0xe5,
+ 0x00, 0xe0, 0x10, 0xa4, 0x00, 0xe4, 0x22, 0x00,
+ 0xe4, 0x01, 0xe0, 0x3d, 0xa5, 0x20, 0x05, 0x00,
+ 0xe5, 0x24, 0x00, 0x25, 0x40, 0x05, 0x20, 0xe5,
+ 0x0f, 0x00, 0x16, 0xeb, 0x00, 0xe5, 0x0f, 0x2f,
+ 0xcb, 0xe5, 0x17, 0xe0, 0x00, 0xeb, 0x01, 0xe0,
+ 0x28, 0xe5, 0x0b, 0x00, 0x25, 0x80, 0x8b, 0xe5,
+ 0x0e, 0xab, 0x40, 0x16, 0xe5, 0x12, 0x80, 0x16,
+ 0xe0, 0x38, 0xe5, 0x30, 0x60, 0x2b, 0x25, 0xeb,
+ 0x08, 0x20, 0xeb, 0x26, 0x05, 0x46, 0x00, 0x26,
+ 0x80, 0x66, 0x65, 0x00, 0x45, 0x00, 0xe5, 0x15,
+ 0x20, 0x46, 0x60, 0x06, 0xeb, 0x01, 0xc0, 0xf6,
+ 0x01, 0xc0, 0xe5, 0x15, 0x2b, 0x16, 0xe5, 0x15,
+ 0x4b, 0xe0, 0x18, 0xe5, 0x00, 0x0f, 0xe5, 0x14,
+ 0x26, 0x60, 0x8b, 0xd6, 0xe0, 0x01, 0xe5, 0x2e,
+ 0x40, 0xd6, 0xe5, 0x0e, 0x20, 0xeb, 0x00, 0xe5,
+ 0x0b, 0x80, 0xeb, 0x00, 0xe5, 0x0a, 0xc0, 0x76,
+ 0xe0, 0x04, 0xcb, 0xe0, 0x48, 0xe5, 0x41, 0xe0,
+ 0x2f, 0xe1, 0x2b, 0xe0, 0x05, 0xe2, 0x2b, 0xc0,
+ 0xab, 0xe5, 0x1c, 0x66, 0xe0, 0x00, 0xe9, 0x02,
+ 0xe0, 0x80, 0x9e, 0xeb, 0x17, 0x00, 0xe5, 0x22,
+ 0x00, 0x26, 0x11, 0x20, 0x25, 0xe0, 0x46, 0xe5,
+ 0x15, 0xeb, 0x02, 0x05, 0xe0, 0x00, 0xe5, 0x0e,
+ 0xe6, 0x03, 0x6b, 0x96, 0xe0, 0x0e, 0xe5, 0x0a,
+ 0x66, 0x76, 0xe0, 0x1e, 0xe5, 0x0d, 0xcb, 0xe0,
+ 0x0c, 0xe5, 0x0f, 0xe0, 0x01, 0x07, 0x06, 0x07,
+ 0xe5, 0x2d, 0xe6, 0x07, 0xd6, 0x60, 0xeb, 0x0c,
+ 0xe9, 0x02, 0x06, 0x25, 0x26, 0x05, 0xe0, 0x01,
+ 0x46, 0x07, 0xe5, 0x25, 0x47, 0x66, 0x27, 0x26,
+ 0x36, 0x1b, 0x76, 0x06, 0xe0, 0x02, 0x1b, 0x20,
+ 0xe5, 0x11, 0xc0, 0xe9, 0x02, 0xa0, 0x46, 0xe5,
+ 0x1c, 0x86, 0x07, 0xe6, 0x00, 0x00, 0xe9, 0x02,
+ 0x76, 0x05, 0x27, 0x05, 0xe0, 0x00, 0xe5, 0x1b,
+ 0x06, 0x36, 0x05, 0xe0, 0x01, 0x26, 0x07, 0xe5,
+ 0x28, 0x47, 0xe6, 0x01, 0x27, 0x65, 0x76, 0x66,
+ 0x16, 0x07, 0x06, 0xe9, 0x02, 0x05, 0x16, 0x05,
+ 0x56, 0x00, 0xeb, 0x0c, 0xe0, 0x03, 0xe5, 0x0a,
+ 0x00, 0xe5, 0x11, 0x47, 0x46, 0x27, 0x06, 0x07,
+ 0x26, 0xb6, 0x06, 0xe0, 0x39, 0xc5, 0x00, 0x05,
+ 0x00, 0x65, 0x00, 0xe5, 0x07, 0x00, 0xe5, 0x02,
+ 0x16, 0xa0, 0xe5, 0x27, 0x06, 0x47, 0xe6, 0x00,
+ 0x80, 0xe9, 0x02, 0xa0, 0x26, 0x27, 0x00, 0xe5,
+ 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5,
+ 0x00, 0x25, 0x00, 0x85, 0x00, 0x26, 0x05, 0x27,
+ 0x06, 0x67, 0x20, 0x27, 0x20, 0x47, 0x20, 0x05,
+ 0xa0, 0x07, 0x80, 0x85, 0x27, 0x20, 0xc6, 0x40,
+ 0x86, 0xe0, 0x80, 0x03, 0xe5, 0x2d, 0x47, 0xe6,
+ 0x00, 0x27, 0x46, 0x07, 0x06, 0x65, 0x96, 0xe9,
+ 0x02, 0x36, 0x00, 0x16, 0x06, 0x45, 0xe0, 0x16,
+ 0xe5, 0x28, 0x47, 0xa6, 0x07, 0x06, 0x67, 0x26,
+ 0x07, 0x26, 0x25, 0x16, 0x05, 0xe0, 0x00, 0xe9,
+ 0x02, 0xe0, 0x80, 0x1e, 0xe5, 0x27, 0x47, 0x66,
+ 0x20, 0x67, 0x26, 0x07, 0x26, 0xf6, 0x0f, 0x65,
+ 0x26, 0xe0, 0x1a, 0xe5, 0x28, 0x47, 0xe6, 0x00,
+ 0x27, 0x06, 0x07, 0x26, 0x56, 0x05, 0xe0, 0x03,
+ 0xe9, 0x02, 0xa0, 0xf6, 0x05, 0xe0, 0x0b, 0xe5,
+ 0x23, 0x06, 0x07, 0x06, 0x27, 0xa6, 0x07, 0x06,
+ 0x05, 0x16, 0xa0, 0xe9, 0x02, 0xe0, 0x2e, 0xe5,
+ 0x13, 0x20, 0x46, 0x27, 0x66, 0x07, 0x86, 0x60,
+ 0xe9, 0x02, 0x2b, 0x56, 0x0f, 0xc5, 0xe0, 0x80,
+ 0x31, 0xe5, 0x24, 0x47, 0xe6, 0x01, 0x07, 0x26,
+ 0x16, 0xe0, 0x5c, 0xe1, 0x18, 0xe2, 0x18, 0xe9,
+ 0x02, 0xeb, 0x01, 0xe0, 0x04, 0xe5, 0x00, 0x20,
+ 0x05, 0x20, 0xe5, 0x00, 0x00, 0x25, 0x00, 0xe5,
+ 0x10, 0xa7, 0x00, 0x27, 0x20, 0x26, 0x07, 0x06,
+ 0x05, 0x07, 0x05, 0x07, 0x06, 0x56, 0xe0, 0x01,
+ 0xe9, 0x02, 0xe0, 0x3e, 0xe5, 0x00, 0x20, 0xe5,
+ 0x1f, 0x47, 0x66, 0x20, 0x26, 0x67, 0x06, 0x05,
+ 0x16, 0x05, 0x07, 0xe0, 0x13, 0x05, 0xe6, 0x02,
+ 0xe5, 0x20, 0xa6, 0x07, 0x05, 0x66, 0xf6, 0x00,
+ 0x06, 0xe0, 0x00, 0x05, 0xa6, 0x27, 0x46, 0xe5,
+ 0x26, 0xe6, 0x05, 0x07, 0x26, 0x56, 0x05, 0x96,
+ 0xe0, 0x05, 0xe5, 0x41, 0xe0, 0x80, 0x7f, 0xe5,
+ 0x01, 0x00, 0xe5, 0x1d, 0x07, 0xc6, 0x00, 0xa6,
+ 0x07, 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, 0x02,
+ 0xeb, 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, 0xe6,
+ 0x0e, 0x00, 0x07, 0xc6, 0x07, 0x26, 0x07, 0x26,
+ 0xe0, 0x41, 0xc5, 0x00, 0x25, 0x00, 0xe5, 0x1e,
+ 0xa6, 0x40, 0x06, 0x00, 0x26, 0x00, 0xc6, 0x05,
+ 0x06, 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xa5, 0x00,
+ 0x25, 0x00, 0xe5, 0x18, 0x87, 0x00, 0x26, 0x00,
+ 0x27, 0x06, 0x07, 0x06, 0x05, 0xc0, 0xe9, 0x02,
+ 0xe0, 0x80, 0xae, 0xe5, 0x0b, 0x26, 0x27, 0x36,
+ 0xe0, 0x80, 0x2f, 0x05, 0xe0, 0x07, 0xeb, 0x0d,
+ 0xef, 0x00, 0x6d, 0xef, 0x09, 0xe0, 0x05, 0x16,
+ 0xe5, 0x83, 0x12, 0xe0, 0x5e, 0xea, 0x67, 0x00,
+ 0x96, 0xe0, 0x03, 0xe5, 0x80, 0x3c, 0xe0, 0x89,
+ 0xc4, 0xe5, 0x59, 0x36, 0xe0, 0x05, 0xe5, 0x83,
+ 0xa7, 0x00, 0xfb, 0x01, 0xe0, 0x8f, 0x3f, 0xe5,
+ 0x81, 0xbf, 0xe0, 0xa1, 0x31, 0xe5, 0x81, 0xb1,
+ 0xc0, 0xe5, 0x17, 0x00, 0xe9, 0x02, 0x60, 0x36,
+ 0xe5, 0x47, 0x00, 0xe9, 0x02, 0xa0, 0xe5, 0x16,
+ 0x20, 0x86, 0x16, 0xe0, 0x02, 0xe5, 0x28, 0xc6,
+ 0x96, 0x6f, 0x64, 0x16, 0x0f, 0xe0, 0x02, 0xe9,
+ 0x02, 0x00, 0xcb, 0x00, 0xe5, 0x0d, 0x80, 0xe5,
+ 0x0b, 0xe0, 0x82, 0x28, 0xe1, 0x18, 0xe2, 0x18,
+ 0xeb, 0x0f, 0x76, 0xe0, 0x5d, 0xe5, 0x43, 0x60,
+ 0x06, 0x05, 0xe7, 0x2f, 0xc0, 0x66, 0xe4, 0x05,
+ 0xe0, 0x38, 0x24, 0x16, 0x04, 0x06, 0xe0, 0x03,
+ 0x27, 0xe0, 0x06, 0xe5, 0x97, 0x70, 0xe0, 0x00,
+ 0xe5, 0x84, 0x4e, 0xe0, 0x22, 0xe5, 0x01, 0xe0,
+ 0xa2, 0x5f, 0x64, 0x00, 0xc4, 0x00, 0x24, 0x00,
+ 0xe5, 0x80, 0x9b, 0xe0, 0x25, 0x45, 0xe0, 0x09,
+ 0x65, 0xe0, 0x00, 0xe5, 0x81, 0x04, 0xe0, 0x88,
+ 0x7c, 0xe5, 0x63, 0x80, 0xe5, 0x05, 0x40, 0xe5,
+ 0x01, 0xc0, 0xe5, 0x02, 0x20, 0x0f, 0x26, 0x16,
+ 0x7b, 0xe0, 0x91, 0xd4, 0xe6, 0x26, 0x20, 0xe6,
+ 0x0f, 0xe0, 0x01, 0xef, 0x6c, 0xe0, 0x34, 0xef,
+ 0x80, 0x6e, 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef,
+ 0x34, 0x27, 0x46, 0x4f, 0xa7, 0xfb, 0x00, 0xe6,
+ 0x00, 0x2f, 0xc6, 0xef, 0x16, 0x66, 0xef, 0x35,
+ 0xe0, 0x0d, 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x80,
+ 0x12, 0xeb, 0x0c, 0xe0, 0x04, 0xef, 0x4f, 0xe0,
+ 0x01, 0xeb, 0x11, 0xe0, 0x7f, 0xe1, 0x12, 0xe2,
+ 0x12, 0xe1, 0x12, 0xc2, 0x00, 0xe2, 0x0a, 0xe1,
+ 0x12, 0xe2, 0x12, 0x01, 0x00, 0x21, 0x20, 0x01,
+ 0x20, 0x21, 0x20, 0x61, 0x00, 0xe1, 0x00, 0x62,
+ 0x00, 0x02, 0x00, 0xc2, 0x00, 0xe2, 0x03, 0xe1,
+ 0x12, 0xe2, 0x12, 0x21, 0x00, 0x61, 0x20, 0xe1,
+ 0x00, 0x00, 0xc1, 0x00, 0xe2, 0x12, 0x21, 0x00,
+ 0x61, 0x00, 0x81, 0x00, 0x01, 0x40, 0xc1, 0x00,
+ 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12,
+ 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12,
+ 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12,
+ 0xe2, 0x14, 0x20, 0xe1, 0x11, 0x0c, 0xe2, 0x11,
+ 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c,
+ 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2,
+ 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1,
+ 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0x3f, 0x20,
+ 0xe9, 0x2a, 0xef, 0x81, 0x78, 0xe6, 0x2f, 0x6f,
+ 0xe6, 0x2a, 0xef, 0x00, 0x06, 0xef, 0x06, 0x06,
+ 0x2f, 0x96, 0xe0, 0x07, 0x86, 0x00, 0xe6, 0x07,
+ 0xe0, 0x83, 0xc8, 0xe2, 0x02, 0x05, 0xe2, 0x0c,
+ 0xe0, 0x80, 0x59, 0xc6, 0x00, 0xe6, 0x09, 0x20,
+ 0xc6, 0x00, 0x26, 0x00, 0x86, 0xe0, 0x80, 0x4d,
+ 0xe5, 0x25, 0x40, 0xc6, 0xc4, 0x20, 0xe9, 0x02,
+ 0x60, 0x05, 0x0f, 0xe0, 0x80, 0xb8, 0xe5, 0x16,
+ 0x06, 0xe0, 0x09, 0xe5, 0x24, 0x66, 0xe9, 0x02,
+ 0x80, 0x0d, 0xe0, 0x84, 0x58, 0xc5, 0x00, 0x65,
+ 0x00, 0x25, 0x00, 0xe5, 0x07, 0x00, 0xe5, 0x80,
+ 0x3d, 0x20, 0xeb, 0x01, 0xc6, 0xe0, 0x21, 0xe1,
+ 0x1a, 0xe2, 0x1a, 0xc6, 0x04, 0x60, 0xe9, 0x02,
+ 0x60, 0x36, 0xe0, 0x82, 0x89, 0xeb, 0x33, 0x0f,
+ 0x4b, 0x0d, 0x6b, 0xe0, 0x44, 0xeb, 0x25, 0x0f,
+ 0xeb, 0x07, 0xe0, 0x80, 0x3a, 0x65, 0x00, 0xe5,
+ 0x13, 0x00, 0x25, 0x00, 0x05, 0x20, 0x05, 0x00,
+ 0xe5, 0x02, 0x00, 0x65, 0x00, 0x05, 0x00, 0x05,
+ 0xa0, 0x05, 0x60, 0x05, 0x00, 0x05, 0x00, 0x05,
+ 0x00, 0x45, 0x00, 0x25, 0x00, 0x05, 0x20, 0x05,
+ 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05,
+ 0x00, 0x25, 0x00, 0x05, 0x20, 0x65, 0x00, 0xc5,
+ 0x00, 0x65, 0x00, 0x65, 0x00, 0x05, 0x00, 0xe5,
+ 0x02, 0x00, 0xe5, 0x09, 0x80, 0x45, 0x00, 0x85,
+ 0x00, 0xe5, 0x09, 0xe0, 0x2c, 0x2c, 0xe0, 0x80,
+ 0x86, 0xef, 0x24, 0x60, 0xef, 0x5c, 0xe0, 0x04,
+ 0xef, 0x07, 0x20, 0xef, 0x07, 0x00, 0xef, 0x07,
+ 0x00, 0xef, 0x1d, 0xe0, 0x02, 0xeb, 0x05, 0xef,
+ 0x80, 0x19, 0xe0, 0x30, 0xef, 0x15, 0xe0, 0x05,
+ 0xef, 0x24, 0x60, 0xef, 0x01, 0xc0, 0x2f, 0xe0,
+ 0x06, 0xaf, 0xe0, 0x80, 0x12, 0xef, 0x80, 0x73,
+ 0x8e, 0xef, 0x82, 0x50, 0x80, 0xef, 0x08, 0x40,
+ 0xef, 0x05, 0x40, 0xef, 0x6c, 0xe0, 0x04, 0xef,
+ 0x51, 0xc0, 0xef, 0x04, 0x60, 0x0f, 0xe0, 0x07,
+ 0xef, 0x04, 0x60, 0xef, 0x30, 0xe0, 0x00, 0xef,
+ 0x02, 0xa0, 0xef, 0x20, 0xe0, 0x00, 0xef, 0x16,
+ 0x20, 0x2f, 0xe0, 0x46, 0xef, 0x80, 0xcc, 0xe0,
+ 0x04, 0xef, 0x06, 0x20, 0x8f, 0x40, 0x8f, 0x40,
+ 0xcf, 0xe0, 0x01, 0xef, 0x15, 0x40, 0xef, 0x03,
+ 0x80, 0xaf, 0xe0, 0x02, 0xef, 0x02, 0xa0, 0xef,
+ 0x00, 0xe0, 0x00, 0xcf, 0xe0, 0x01, 0xef, 0x80,
+ 0x0b, 0x00, 0xef, 0x2f, 0xe0, 0x1d, 0xe9, 0x02,
+ 0xe0, 0x83, 0x7e, 0xe5, 0xc0, 0x66, 0x58, 0xe0,
+ 0x18, 0xe5, 0x8f, 0xb1, 0xc0, 0xe5, 0x80, 0x56,
+ 0x20, 0xe5, 0x95, 0xfa, 0xe0, 0x06, 0xe5, 0x9c,
+ 0xa9, 0xe0, 0x8b, 0x97, 0xe5, 0x81, 0x96, 0xe0,
+ 0x85, 0x5a, 0xe5, 0x92, 0xc3, 0xe0, 0xca, 0xac,
+ 0x2e, 0x1b, 0xe0, 0x16, 0xfb, 0x58, 0xe0, 0x78,
+ 0xe6, 0x80, 0x68, 0xe0, 0xc0, 0xbd, 0x88, 0xfd,
+ 0xc0, 0xbf, 0x76, 0x20, 0xfd, 0xc0, 0xbf, 0x76,
+ 0x20,
+};
+
+typedef enum {
+ UNICODE_SCRIPT_Unknown,
+ UNICODE_SCRIPT_Adlam,
+ UNICODE_SCRIPT_Ahom,
+ UNICODE_SCRIPT_Anatolian_Hieroglyphs,
+ UNICODE_SCRIPT_Arabic,
+ UNICODE_SCRIPT_Armenian,
+ UNICODE_SCRIPT_Avestan,
+ UNICODE_SCRIPT_Balinese,
+ UNICODE_SCRIPT_Bamum,
+ UNICODE_SCRIPT_Bassa_Vah,
+ UNICODE_SCRIPT_Batak,
+ UNICODE_SCRIPT_Bengali,
+ UNICODE_SCRIPT_Bhaiksuki,
+ UNICODE_SCRIPT_Bopomofo,
+ UNICODE_SCRIPT_Brahmi,
+ UNICODE_SCRIPT_Braille,
+ UNICODE_SCRIPT_Buginese,
+ UNICODE_SCRIPT_Buhid,
+ UNICODE_SCRIPT_Canadian_Aboriginal,
+ UNICODE_SCRIPT_Carian,
+ UNICODE_SCRIPT_Caucasian_Albanian,
+ UNICODE_SCRIPT_Chakma,
+ UNICODE_SCRIPT_Cham,
+ UNICODE_SCRIPT_Cherokee,
+ UNICODE_SCRIPT_Chorasmian,
+ UNICODE_SCRIPT_Common,
+ UNICODE_SCRIPT_Coptic,
+ UNICODE_SCRIPT_Cuneiform,
+ UNICODE_SCRIPT_Cypriot,
+ UNICODE_SCRIPT_Cyrillic,
+ UNICODE_SCRIPT_Cypro_Minoan,
+ UNICODE_SCRIPT_Deseret,
+ UNICODE_SCRIPT_Devanagari,
+ UNICODE_SCRIPT_Dives_Akuru,
+ UNICODE_SCRIPT_Dogra,
+ UNICODE_SCRIPT_Duployan,
+ UNICODE_SCRIPT_Egyptian_Hieroglyphs,
+ UNICODE_SCRIPT_Elbasan,
+ UNICODE_SCRIPT_Elymaic,
+ UNICODE_SCRIPT_Ethiopic,
+ UNICODE_SCRIPT_Georgian,
+ UNICODE_SCRIPT_Glagolitic,
+ UNICODE_SCRIPT_Gothic,
+ UNICODE_SCRIPT_Grantha,
+ UNICODE_SCRIPT_Greek,
+ UNICODE_SCRIPT_Gujarati,
+ UNICODE_SCRIPT_Gunjala_Gondi,
+ UNICODE_SCRIPT_Gurmukhi,
+ UNICODE_SCRIPT_Han,
+ UNICODE_SCRIPT_Hangul,
+ UNICODE_SCRIPT_Hanifi_Rohingya,
+ UNICODE_SCRIPT_Hanunoo,
+ UNICODE_SCRIPT_Hatran,
+ UNICODE_SCRIPT_Hebrew,
+ UNICODE_SCRIPT_Hiragana,
+ UNICODE_SCRIPT_Imperial_Aramaic,
+ UNICODE_SCRIPT_Inherited,
+ UNICODE_SCRIPT_Inscriptional_Pahlavi,
+ UNICODE_SCRIPT_Inscriptional_Parthian,
+ UNICODE_SCRIPT_Javanese,
+ UNICODE_SCRIPT_Kaithi,
+ UNICODE_SCRIPT_Kannada,
+ UNICODE_SCRIPT_Katakana,
+ UNICODE_SCRIPT_Kayah_Li,
+ UNICODE_SCRIPT_Kharoshthi,
+ UNICODE_SCRIPT_Khmer,
+ UNICODE_SCRIPT_Khojki,
+ UNICODE_SCRIPT_Khitan_Small_Script,
+ UNICODE_SCRIPT_Khudawadi,
+ UNICODE_SCRIPT_Lao,
+ UNICODE_SCRIPT_Latin,
+ UNICODE_SCRIPT_Lepcha,
+ UNICODE_SCRIPT_Limbu,
+ UNICODE_SCRIPT_Linear_A,
+ UNICODE_SCRIPT_Linear_B,
+ UNICODE_SCRIPT_Lisu,
+ UNICODE_SCRIPT_Lycian,
+ UNICODE_SCRIPT_Lydian,
+ UNICODE_SCRIPT_Makasar,
+ UNICODE_SCRIPT_Mahajani,
+ UNICODE_SCRIPT_Malayalam,
+ UNICODE_SCRIPT_Mandaic,
+ UNICODE_SCRIPT_Manichaean,
+ UNICODE_SCRIPT_Marchen,
+ UNICODE_SCRIPT_Masaram_Gondi,
+ UNICODE_SCRIPT_Medefaidrin,
+ UNICODE_SCRIPT_Meetei_Mayek,
+ UNICODE_SCRIPT_Mende_Kikakui,
+ UNICODE_SCRIPT_Meroitic_Cursive,
+ UNICODE_SCRIPT_Meroitic_Hieroglyphs,
+ UNICODE_SCRIPT_Miao,
+ UNICODE_SCRIPT_Modi,
+ UNICODE_SCRIPT_Mongolian,
+ UNICODE_SCRIPT_Mro,
+ UNICODE_SCRIPT_Multani,
+ UNICODE_SCRIPT_Myanmar,
+ UNICODE_SCRIPT_Nabataean,
+ UNICODE_SCRIPT_Nandinagari,
+ UNICODE_SCRIPT_New_Tai_Lue,
+ UNICODE_SCRIPT_Newa,
+ UNICODE_SCRIPT_Nko,
+ UNICODE_SCRIPT_Nushu,
+ UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong,
+ UNICODE_SCRIPT_Ogham,
+ UNICODE_SCRIPT_Ol_Chiki,
+ UNICODE_SCRIPT_Old_Hungarian,
+ UNICODE_SCRIPT_Old_Italic,
+ UNICODE_SCRIPT_Old_North_Arabian,
+ UNICODE_SCRIPT_Old_Permic,
+ UNICODE_SCRIPT_Old_Persian,
+ UNICODE_SCRIPT_Old_Sogdian,
+ UNICODE_SCRIPT_Old_South_Arabian,
+ UNICODE_SCRIPT_Old_Turkic,
+ UNICODE_SCRIPT_Old_Uyghur,
+ UNICODE_SCRIPT_Oriya,
+ UNICODE_SCRIPT_Osage,
+ UNICODE_SCRIPT_Osmanya,
+ UNICODE_SCRIPT_Pahawh_Hmong,
+ UNICODE_SCRIPT_Palmyrene,
+ UNICODE_SCRIPT_Pau_Cin_Hau,
+ UNICODE_SCRIPT_Phags_Pa,
+ UNICODE_SCRIPT_Phoenician,
+ UNICODE_SCRIPT_Psalter_Pahlavi,
+ UNICODE_SCRIPT_Rejang,
+ UNICODE_SCRIPT_Runic,
+ UNICODE_SCRIPT_Samaritan,
+ UNICODE_SCRIPT_Saurashtra,
+ UNICODE_SCRIPT_Sharada,
+ UNICODE_SCRIPT_Shavian,
+ UNICODE_SCRIPT_Siddham,
+ UNICODE_SCRIPT_SignWriting,
+ UNICODE_SCRIPT_Sinhala,
+ UNICODE_SCRIPT_Sogdian,
+ UNICODE_SCRIPT_Sora_Sompeng,
+ UNICODE_SCRIPT_Soyombo,
+ UNICODE_SCRIPT_Sundanese,
+ UNICODE_SCRIPT_Syloti_Nagri,
+ UNICODE_SCRIPT_Syriac,
+ UNICODE_SCRIPT_Tagalog,
+ UNICODE_SCRIPT_Tagbanwa,
+ UNICODE_SCRIPT_Tai_Le,
+ UNICODE_SCRIPT_Tai_Tham,
+ UNICODE_SCRIPT_Tai_Viet,
+ UNICODE_SCRIPT_Takri,
+ UNICODE_SCRIPT_Tamil,
+ UNICODE_SCRIPT_Tangut,
+ UNICODE_SCRIPT_Telugu,
+ UNICODE_SCRIPT_Thaana,
+ UNICODE_SCRIPT_Thai,
+ UNICODE_SCRIPT_Tibetan,
+ UNICODE_SCRIPT_Tifinagh,
+ UNICODE_SCRIPT_Tirhuta,
+ UNICODE_SCRIPT_Tangsa,
+ UNICODE_SCRIPT_Toto,
+ UNICODE_SCRIPT_Ugaritic,
+ UNICODE_SCRIPT_Vai,
+ UNICODE_SCRIPT_Vithkuqi,
+ UNICODE_SCRIPT_Wancho,
+ UNICODE_SCRIPT_Warang_Citi,
+ UNICODE_SCRIPT_Yezidi,
+ UNICODE_SCRIPT_Yi,
+ UNICODE_SCRIPT_Zanabazar_Square,
+ UNICODE_SCRIPT_COUNT,
+} UnicodeScriptEnum;
+
+static const char unicode_script_name_table[] =
+ "Adlam,Adlm" "\0"
+ "Ahom,Ahom" "\0"
+ "Anatolian_Hieroglyphs,Hluw" "\0"
+ "Arabic,Arab" "\0"
+ "Armenian,Armn" "\0"
+ "Avestan,Avst" "\0"
+ "Balinese,Bali" "\0"
+ "Bamum,Bamu" "\0"
+ "Bassa_Vah,Bass" "\0"
+ "Batak,Batk" "\0"
+ "Bengali,Beng" "\0"
+ "Bhaiksuki,Bhks" "\0"
+ "Bopomofo,Bopo" "\0"
+ "Brahmi,Brah" "\0"
+ "Braille,Brai" "\0"
+ "Buginese,Bugi" "\0"
+ "Buhid,Buhd" "\0"
+ "Canadian_Aboriginal,Cans" "\0"
+ "Carian,Cari" "\0"
+ "Caucasian_Albanian,Aghb" "\0"
+ "Chakma,Cakm" "\0"
+ "Cham,Cham" "\0"
+ "Cherokee,Cher" "\0"
+ "Chorasmian,Chrs" "\0"
+ "Common,Zyyy" "\0"
+ "Coptic,Copt,Qaac" "\0"
+ "Cuneiform,Xsux" "\0"
+ "Cypriot,Cprt" "\0"
+ "Cyrillic,Cyrl" "\0"
+ "Cypro_Minoan,Cpmn" "\0"
+ "Deseret,Dsrt" "\0"
+ "Devanagari,Deva" "\0"
+ "Dives_Akuru,Diak" "\0"
+ "Dogra,Dogr" "\0"
+ "Duployan,Dupl" "\0"
+ "Egyptian_Hieroglyphs,Egyp" "\0"
+ "Elbasan,Elba" "\0"
+ "Elymaic,Elym" "\0"
+ "Ethiopic,Ethi" "\0"
+ "Georgian,Geor" "\0"
+ "Glagolitic,Glag" "\0"
+ "Gothic,Goth" "\0"
+ "Grantha,Gran" "\0"
+ "Greek,Grek" "\0"
+ "Gujarati,Gujr" "\0"
+ "Gunjala_Gondi,Gong" "\0"
+ "Gurmukhi,Guru" "\0"
+ "Han,Hani" "\0"
+ "Hangul,Hang" "\0"
+ "Hanifi_Rohingya,Rohg" "\0"
+ "Hanunoo,Hano" "\0"
+ "Hatran,Hatr" "\0"
+ "Hebrew,Hebr" "\0"
+ "Hiragana,Hira" "\0"
+ "Imperial_Aramaic,Armi" "\0"
+ "Inherited,Zinh,Qaai" "\0"
+ "Inscriptional_Pahlavi,Phli" "\0"
+ "Inscriptional_Parthian,Prti" "\0"
+ "Javanese,Java" "\0"
+ "Kaithi,Kthi" "\0"
+ "Kannada,Knda" "\0"
+ "Katakana,Kana" "\0"
+ "Kayah_Li,Kali" "\0"
+ "Kharoshthi,Khar" "\0"
+ "Khmer,Khmr" "\0"
+ "Khojki,Khoj" "\0"
+ "Khitan_Small_Script,Kits" "\0"
+ "Khudawadi,Sind" "\0"
+ "Lao,Laoo" "\0"
+ "Latin,Latn" "\0"
+ "Lepcha,Lepc" "\0"
+ "Limbu,Limb" "\0"
+ "Linear_A,Lina" "\0"
+ "Linear_B,Linb" "\0"
+ "Lisu,Lisu" "\0"
+ "Lycian,Lyci" "\0"
+ "Lydian,Lydi" "\0"
+ "Makasar,Maka" "\0"
+ "Mahajani,Mahj" "\0"
+ "Malayalam,Mlym" "\0"
+ "Mandaic,Mand" "\0"
+ "Manichaean,Mani" "\0"
+ "Marchen,Marc" "\0"
+ "Masaram_Gondi,Gonm" "\0"
+ "Medefaidrin,Medf" "\0"
+ "Meetei_Mayek,Mtei" "\0"
+ "Mende_Kikakui,Mend" "\0"
+ "Meroitic_Cursive,Merc" "\0"
+ "Meroitic_Hieroglyphs,Mero" "\0"
+ "Miao,Plrd" "\0"
+ "Modi,Modi" "\0"
+ "Mongolian,Mong" "\0"
+ "Mro,Mroo" "\0"
+ "Multani,Mult" "\0"
+ "Myanmar,Mymr" "\0"
+ "Nabataean,Nbat" "\0"
+ "Nandinagari,Nand" "\0"
+ "New_Tai_Lue,Talu" "\0"
+ "Newa,Newa" "\0"
+ "Nko,Nkoo" "\0"
+ "Nushu,Nshu" "\0"
+ "Nyiakeng_Puachue_Hmong,Hmnp" "\0"
+ "Ogham,Ogam" "\0"
+ "Ol_Chiki,Olck" "\0"
+ "Old_Hungarian,Hung" "\0"
+ "Old_Italic,Ital" "\0"
+ "Old_North_Arabian,Narb" "\0"
+ "Old_Permic,Perm" "\0"
+ "Old_Persian,Xpeo" "\0"
+ "Old_Sogdian,Sogo" "\0"
+ "Old_South_Arabian,Sarb" "\0"
+ "Old_Turkic,Orkh" "\0"
+ "Old_Uyghur,Ougr" "\0"
+ "Oriya,Orya" "\0"
+ "Osage,Osge" "\0"
+ "Osmanya,Osma" "\0"
+ "Pahawh_Hmong,Hmng" "\0"
+ "Palmyrene,Palm" "\0"
+ "Pau_Cin_Hau,Pauc" "\0"
+ "Phags_Pa,Phag" "\0"
+ "Phoenician,Phnx" "\0"
+ "Psalter_Pahlavi,Phlp" "\0"
+ "Rejang,Rjng" "\0"
+ "Runic,Runr" "\0"
+ "Samaritan,Samr" "\0"
+ "Saurashtra,Saur" "\0"
+ "Sharada,Shrd" "\0"
+ "Shavian,Shaw" "\0"
+ "Siddham,Sidd" "\0"
+ "SignWriting,Sgnw" "\0"
+ "Sinhala,Sinh" "\0"
+ "Sogdian,Sogd" "\0"
+ "Sora_Sompeng,Sora" "\0"
+ "Soyombo,Soyo" "\0"
+ "Sundanese,Sund" "\0"
+ "Syloti_Nagri,Sylo" "\0"
+ "Syriac,Syrc" "\0"
+ "Tagalog,Tglg" "\0"
+ "Tagbanwa,Tagb" "\0"
+ "Tai_Le,Tale" "\0"
+ "Tai_Tham,Lana" "\0"
+ "Tai_Viet,Tavt" "\0"
+ "Takri,Takr" "\0"
+ "Tamil,Taml" "\0"
+ "Tangut,Tang" "\0"
+ "Telugu,Telu" "\0"
+ "Thaana,Thaa" "\0"
+ "Thai,Thai" "\0"
+ "Tibetan,Tibt" "\0"
+ "Tifinagh,Tfng" "\0"
+ "Tirhuta,Tirh" "\0"
+ "Tangsa,Tnsa" "\0"
+ "Toto,Toto" "\0"
+ "Ugaritic,Ugar" "\0"
+ "Vai,Vaii" "\0"
+ "Vithkuqi,Vith" "\0"
+ "Wancho,Wcho" "\0"
+ "Warang_Citi,Wara" "\0"
+ "Yezidi,Yezi" "\0"
+ "Yi,Yiii" "\0"
+ "Zanabazar_Square,Zanb" "\0"
+;
+
+static const uint8_t unicode_script_table[2690] = {
+ 0xc0, 0x19, 0x99, 0x46, 0x85, 0x19, 0x99, 0x46,
+ 0xae, 0x19, 0x80, 0x46, 0x8e, 0x19, 0x80, 0x46,
+ 0x84, 0x19, 0x96, 0x46, 0x80, 0x19, 0x9e, 0x46,
+ 0x80, 0x19, 0xe1, 0x60, 0x46, 0xa6, 0x19, 0x84,
+ 0x46, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0,
+ 0x0f, 0x38, 0x83, 0x2c, 0x80, 0x19, 0x82, 0x2c,
+ 0x01, 0x83, 0x2c, 0x80, 0x19, 0x80, 0x2c, 0x03,
+ 0x80, 0x2c, 0x80, 0x19, 0x80, 0x2c, 0x80, 0x19,
+ 0x82, 0x2c, 0x00, 0x80, 0x2c, 0x00, 0x93, 0x2c,
+ 0x00, 0xbe, 0x2c, 0x8d, 0x1a, 0x8f, 0x2c, 0xe0,
+ 0x24, 0x1d, 0x81, 0x38, 0xe0, 0x48, 0x1d, 0x00,
+ 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05,
+ 0x00, 0xb6, 0x35, 0x07, 0x9a, 0x35, 0x03, 0x85,
+ 0x35, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04,
+ 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x82, 0x04,
+ 0x80, 0x19, 0x9f, 0x04, 0x80, 0x19, 0x89, 0x04,
+ 0x8a, 0x38, 0x99, 0x04, 0x80, 0x38, 0xe0, 0x0b,
+ 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x89, 0x00,
+ 0xbb, 0x89, 0x01, 0x82, 0x89, 0xaf, 0x04, 0xb1,
+ 0x93, 0x0d, 0xba, 0x64, 0x01, 0x82, 0x64, 0xad,
+ 0x7d, 0x01, 0x8e, 0x7d, 0x00, 0x9b, 0x51, 0x01,
+ 0x80, 0x51, 0x00, 0x8a, 0x89, 0x04, 0x9e, 0x04,
+ 0x00, 0x81, 0x04, 0x05, 0xc9, 0x04, 0x80, 0x19,
+ 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x38, 0x8e, 0x20,
+ 0x81, 0x19, 0x99, 0x20, 0x83, 0x0b, 0x00, 0x87,
+ 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x95, 0x0b, 0x00,
+ 0x86, 0x0b, 0x00, 0x80, 0x0b, 0x02, 0x83, 0x0b,
+ 0x01, 0x88, 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x83,
+ 0x0b, 0x07, 0x80, 0x0b, 0x03, 0x81, 0x0b, 0x00,
+ 0x84, 0x0b, 0x01, 0x98, 0x0b, 0x01, 0x82, 0x2f,
+ 0x00, 0x85, 0x2f, 0x03, 0x81, 0x2f, 0x01, 0x95,
+ 0x2f, 0x00, 0x86, 0x2f, 0x00, 0x81, 0x2f, 0x00,
+ 0x81, 0x2f, 0x00, 0x81, 0x2f, 0x01, 0x80, 0x2f,
+ 0x00, 0x84, 0x2f, 0x03, 0x81, 0x2f, 0x01, 0x82,
+ 0x2f, 0x02, 0x80, 0x2f, 0x06, 0x83, 0x2f, 0x00,
+ 0x80, 0x2f, 0x06, 0x90, 0x2f, 0x09, 0x82, 0x2d,
+ 0x00, 0x88, 0x2d, 0x00, 0x82, 0x2d, 0x00, 0x95,
+ 0x2d, 0x00, 0x86, 0x2d, 0x00, 0x81, 0x2d, 0x00,
+ 0x84, 0x2d, 0x01, 0x89, 0x2d, 0x00, 0x82, 0x2d,
+ 0x00, 0x82, 0x2d, 0x01, 0x80, 0x2d, 0x0e, 0x83,
+ 0x2d, 0x01, 0x8b, 0x2d, 0x06, 0x86, 0x2d, 0x00,
+ 0x82, 0x72, 0x00, 0x87, 0x72, 0x01, 0x81, 0x72,
+ 0x01, 0x95, 0x72, 0x00, 0x86, 0x72, 0x00, 0x81,
+ 0x72, 0x00, 0x84, 0x72, 0x01, 0x88, 0x72, 0x01,
+ 0x81, 0x72, 0x01, 0x82, 0x72, 0x06, 0x82, 0x72,
+ 0x03, 0x81, 0x72, 0x00, 0x84, 0x72, 0x01, 0x91,
+ 0x72, 0x09, 0x81, 0x90, 0x00, 0x85, 0x90, 0x02,
+ 0x82, 0x90, 0x00, 0x83, 0x90, 0x02, 0x81, 0x90,
+ 0x00, 0x80, 0x90, 0x00, 0x81, 0x90, 0x02, 0x81,
+ 0x90, 0x02, 0x82, 0x90, 0x02, 0x8b, 0x90, 0x03,
+ 0x84, 0x90, 0x02, 0x82, 0x90, 0x00, 0x83, 0x90,
+ 0x01, 0x80, 0x90, 0x05, 0x80, 0x90, 0x0d, 0x94,
+ 0x90, 0x04, 0x8c, 0x92, 0x00, 0x82, 0x92, 0x00,
+ 0x96, 0x92, 0x00, 0x8f, 0x92, 0x01, 0x88, 0x92,
+ 0x00, 0x82, 0x92, 0x00, 0x83, 0x92, 0x06, 0x81,
+ 0x92, 0x00, 0x82, 0x92, 0x01, 0x80, 0x92, 0x01,
+ 0x83, 0x92, 0x01, 0x89, 0x92, 0x06, 0x88, 0x92,
+ 0x8c, 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x96, 0x3d,
+ 0x00, 0x89, 0x3d, 0x00, 0x84, 0x3d, 0x01, 0x88,
+ 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x83, 0x3d, 0x06,
+ 0x81, 0x3d, 0x05, 0x81, 0x3d, 0x00, 0x83, 0x3d,
+ 0x01, 0x89, 0x3d, 0x00, 0x81, 0x3d, 0x0c, 0x8c,
+ 0x50, 0x00, 0x82, 0x50, 0x00, 0xb2, 0x50, 0x00,
+ 0x82, 0x50, 0x00, 0x85, 0x50, 0x03, 0x8f, 0x50,
+ 0x01, 0x99, 0x50, 0x00, 0x82, 0x83, 0x00, 0x91,
+ 0x83, 0x02, 0x97, 0x83, 0x00, 0x88, 0x83, 0x00,
+ 0x80, 0x83, 0x01, 0x86, 0x83, 0x02, 0x80, 0x83,
+ 0x03, 0x85, 0x83, 0x00, 0x80, 0x83, 0x00, 0x87,
+ 0x83, 0x05, 0x89, 0x83, 0x01, 0x82, 0x83, 0x0b,
+ 0xb9, 0x94, 0x03, 0x80, 0x19, 0x9b, 0x94, 0x24,
+ 0x81, 0x45, 0x00, 0x80, 0x45, 0x00, 0x84, 0x45,
+ 0x00, 0x97, 0x45, 0x00, 0x80, 0x45, 0x00, 0x96,
+ 0x45, 0x01, 0x84, 0x45, 0x00, 0x80, 0x45, 0x00,
+ 0x85, 0x45, 0x01, 0x89, 0x45, 0x01, 0x83, 0x45,
+ 0x1f, 0xc7, 0x95, 0x00, 0xa3, 0x95, 0x03, 0xa6,
+ 0x95, 0x00, 0xa3, 0x95, 0x00, 0x8e, 0x95, 0x00,
+ 0x86, 0x95, 0x83, 0x19, 0x81, 0x95, 0x24, 0xe0,
+ 0x3f, 0x5f, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04,
+ 0x80, 0x28, 0x01, 0xaa, 0x28, 0x80, 0x19, 0x83,
+ 0x28, 0xe0, 0x9f, 0x31, 0xc8, 0x27, 0x00, 0x83,
+ 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00,
+ 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27,
+ 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86,
+ 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01,
+ 0x8e, 0x27, 0x00, 0xb8, 0x27, 0x00, 0x83, 0x27,
+ 0x01, 0xc2, 0x27, 0x01, 0x9f, 0x27, 0x02, 0x99,
+ 0x27, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01,
+ 0xe2, 0x1f, 0x12, 0x9c, 0x67, 0x02, 0xca, 0x7c,
+ 0x82, 0x19, 0x8a, 0x7c, 0x06, 0x95, 0x8a, 0x08,
+ 0x80, 0x8a, 0x94, 0x33, 0x81, 0x19, 0x08, 0x93,
+ 0x11, 0x0b, 0x8c, 0x8b, 0x00, 0x82, 0x8b, 0x00,
+ 0x81, 0x8b, 0x0b, 0xdd, 0x41, 0x01, 0x89, 0x41,
+ 0x05, 0x89, 0x41, 0x05, 0x81, 0x5c, 0x81, 0x19,
+ 0x80, 0x5c, 0x80, 0x19, 0x93, 0x5c, 0x05, 0xd8,
+ 0x5c, 0x06, 0xaa, 0x5c, 0x04, 0xc5, 0x12, 0x09,
+ 0x9e, 0x48, 0x00, 0x8b, 0x48, 0x03, 0x8b, 0x48,
+ 0x03, 0x80, 0x48, 0x02, 0x8b, 0x48, 0x9d, 0x8c,
+ 0x01, 0x84, 0x8c, 0x0a, 0xab, 0x62, 0x03, 0x99,
+ 0x62, 0x05, 0x8a, 0x62, 0x02, 0x81, 0x62, 0x9f,
+ 0x41, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x8d,
+ 0x00, 0x9c, 0x8d, 0x01, 0x8a, 0x8d, 0x05, 0x89,
+ 0x8d, 0x05, 0x8d, 0x8d, 0x01, 0x9e, 0x38, 0x30,
+ 0xcc, 0x07, 0x02, 0xae, 0x07, 0x00, 0xbf, 0x87,
+ 0xb3, 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x47, 0x02,
+ 0x8e, 0x47, 0x02, 0x82, 0x47, 0xaf, 0x68, 0x88,
+ 0x1d, 0x06, 0xaa, 0x28, 0x01, 0x82, 0x28, 0x87,
+ 0x87, 0x07, 0x82, 0x38, 0x80, 0x19, 0x8c, 0x38,
+ 0x80, 0x19, 0x86, 0x38, 0x83, 0x19, 0x80, 0x38,
+ 0x85, 0x19, 0x80, 0x38, 0x82, 0x19, 0x81, 0x38,
+ 0x80, 0x19, 0x04, 0xa5, 0x46, 0x84, 0x2c, 0x80,
+ 0x1d, 0xb0, 0x46, 0x84, 0x2c, 0x83, 0x46, 0x84,
+ 0x2c, 0x8c, 0x46, 0x80, 0x1d, 0xc5, 0x46, 0x80,
+ 0x2c, 0xbf, 0x38, 0xe0, 0x9f, 0x46, 0x95, 0x2c,
+ 0x01, 0x85, 0x2c, 0x01, 0xa5, 0x2c, 0x01, 0x85,
+ 0x2c, 0x01, 0x87, 0x2c, 0x00, 0x80, 0x2c, 0x00,
+ 0x80, 0x2c, 0x00, 0x80, 0x2c, 0x00, 0x9e, 0x2c,
+ 0x01, 0xb4, 0x2c, 0x00, 0x8e, 0x2c, 0x00, 0x8d,
+ 0x2c, 0x01, 0x85, 0x2c, 0x00, 0x92, 0x2c, 0x01,
+ 0x82, 0x2c, 0x00, 0x88, 0x2c, 0x00, 0x8b, 0x19,
+ 0x81, 0x38, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80,
+ 0x46, 0x01, 0x8a, 0x19, 0x80, 0x46, 0x8e, 0x19,
+ 0x00, 0x8c, 0x46, 0x02, 0xa0, 0x19, 0x0e, 0xa0,
+ 0x38, 0x0e, 0xa5, 0x19, 0x80, 0x2c, 0x82, 0x19,
+ 0x81, 0x46, 0x85, 0x19, 0x80, 0x46, 0x9a, 0x19,
+ 0x80, 0x46, 0x90, 0x19, 0xa8, 0x46, 0x82, 0x19,
+ 0x03, 0xe2, 0x36, 0x19, 0x18, 0x8a, 0x19, 0x14,
+ 0xe3, 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13,
+ 0x19, 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19,
+ 0xdf, 0x29, 0x9f, 0x46, 0xe0, 0x13, 0x1a, 0x04,
+ 0x86, 0x1a, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04,
+ 0x80, 0x28, 0x01, 0xb7, 0x96, 0x06, 0x81, 0x96,
+ 0x0d, 0x80, 0x96, 0x96, 0x27, 0x08, 0x86, 0x27,
+ 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86,
+ 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00,
+ 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d,
+ 0xdd, 0x19, 0x21, 0x99, 0x30, 0x00, 0xd8, 0x30,
+ 0x0b, 0xe0, 0x75, 0x30, 0x19, 0x8b, 0x19, 0x03,
+ 0x84, 0x19, 0x80, 0x30, 0x80, 0x19, 0x80, 0x30,
+ 0x98, 0x19, 0x88, 0x30, 0x83, 0x38, 0x81, 0x31,
+ 0x87, 0x19, 0x83, 0x30, 0x83, 0x19, 0x00, 0xd5,
+ 0x36, 0x01, 0x81, 0x38, 0x81, 0x19, 0x82, 0x36,
+ 0x80, 0x19, 0xd9, 0x3e, 0x81, 0x19, 0x82, 0x3e,
+ 0x04, 0xaa, 0x0d, 0x00, 0xdd, 0x31, 0x00, 0x8f,
+ 0x19, 0x9f, 0x0d, 0xa3, 0x19, 0x0b, 0x8f, 0x3e,
+ 0x9e, 0x31, 0x00, 0xbf, 0x19, 0x9e, 0x31, 0xd0,
+ 0x19, 0xae, 0x3e, 0x80, 0x19, 0xd7, 0x3e, 0xe0,
+ 0x47, 0x19, 0xf0, 0x09, 0x5f, 0x30, 0xbf, 0x19,
+ 0xf0, 0x41, 0x9f, 0x30, 0xe4, 0x2c, 0xa0, 0x02,
+ 0xb6, 0xa0, 0x08, 0xaf, 0x4b, 0xe0, 0xcb, 0x9b,
+ 0x13, 0xdf, 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19,
+ 0xe0, 0x05, 0x46, 0x82, 0x19, 0xbf, 0x46, 0x04,
+ 0x81, 0x46, 0x00, 0x80, 0x46, 0x00, 0x84, 0x46,
+ 0x17, 0x8d, 0x46, 0xac, 0x88, 0x02, 0x89, 0x19,
+ 0x05, 0xb7, 0x78, 0x07, 0xc5, 0x7e, 0x07, 0x8b,
+ 0x7e, 0x05, 0x9f, 0x20, 0xad, 0x3f, 0x80, 0x19,
+ 0x80, 0x3f, 0xa3, 0x7b, 0x0a, 0x80, 0x7b, 0x9c,
+ 0x31, 0x02, 0xcd, 0x3b, 0x00, 0x80, 0x19, 0x89,
+ 0x3b, 0x03, 0x81, 0x3b, 0x9e, 0x5f, 0x00, 0xb6,
+ 0x16, 0x08, 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01,
+ 0x83, 0x16, 0x9f, 0x5f, 0xc2, 0x8e, 0x17, 0x84,
+ 0x8e, 0x96, 0x56, 0x09, 0x85, 0x27, 0x01, 0x85,
+ 0x27, 0x01, 0x85, 0x27, 0x08, 0x86, 0x27, 0x00,
+ 0x86, 0x27, 0x00, 0xaa, 0x46, 0x80, 0x19, 0x88,
+ 0x46, 0x80, 0x2c, 0x83, 0x46, 0x81, 0x19, 0x03,
+ 0xcf, 0x17, 0xad, 0x56, 0x01, 0x89, 0x56, 0x05,
+ 0xf0, 0x1b, 0x43, 0x31, 0x0b, 0x96, 0x31, 0x03,
+ 0xb0, 0x31, 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x30,
+ 0x01, 0xe0, 0x09, 0x30, 0x25, 0x86, 0x46, 0x0b,
+ 0x84, 0x05, 0x04, 0x99, 0x35, 0x00, 0x84, 0x35,
+ 0x00, 0x80, 0x35, 0x00, 0x81, 0x35, 0x00, 0x81,
+ 0x35, 0x00, 0x89, 0x35, 0xe0, 0x12, 0x04, 0x0f,
+ 0xe1, 0x0a, 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01,
+ 0xb5, 0x04, 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04,
+ 0x8f, 0x38, 0x89, 0x19, 0x05, 0x8d, 0x38, 0x81,
+ 0x1d, 0xa2, 0x19, 0x00, 0x92, 0x19, 0x00, 0x83,
+ 0x19, 0x03, 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04,
+ 0x01, 0x80, 0x19, 0x00, 0x9f, 0x19, 0x99, 0x46,
+ 0x85, 0x19, 0x99, 0x46, 0x8a, 0x19, 0x89, 0x3e,
+ 0x80, 0x19, 0xac, 0x3e, 0x81, 0x19, 0x9e, 0x31,
+ 0x02, 0x85, 0x31, 0x01, 0x85, 0x31, 0x01, 0x85,
+ 0x31, 0x01, 0x82, 0x31, 0x02, 0x86, 0x19, 0x00,
+ 0x86, 0x19, 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4a,
+ 0x00, 0x99, 0x4a, 0x00, 0x92, 0x4a, 0x00, 0x81,
+ 0x4a, 0x00, 0x8e, 0x4a, 0x01, 0x8d, 0x4a, 0x21,
+ 0xe0, 0x1a, 0x4a, 0x04, 0x82, 0x19, 0x03, 0xac,
+ 0x19, 0x02, 0x88, 0x19, 0xce, 0x2c, 0x00, 0x8c,
+ 0x19, 0x02, 0x80, 0x2c, 0x2e, 0xac, 0x19, 0x80,
+ 0x38, 0x60, 0x21, 0x9c, 0x4c, 0x02, 0xb0, 0x13,
+ 0x0e, 0x80, 0x38, 0x9a, 0x19, 0x03, 0xa3, 0x6a,
+ 0x08, 0x82, 0x6a, 0x9a, 0x2a, 0x04, 0xaa, 0x6c,
+ 0x04, 0x9d, 0x9a, 0x00, 0x80, 0x9a, 0xa3, 0x6d,
+ 0x03, 0x8d, 0x6d, 0x29, 0xcf, 0x1f, 0xaf, 0x80,
+ 0x9d, 0x74, 0x01, 0x89, 0x74, 0x05, 0xa3, 0x73,
+ 0x03, 0xa3, 0x73, 0x03, 0xa7, 0x25, 0x07, 0xb3,
+ 0x14, 0x0a, 0x80, 0x14, 0x8a, 0x9c, 0x00, 0x8e,
+ 0x9c, 0x00, 0x86, 0x9c, 0x00, 0x81, 0x9c, 0x00,
+ 0x8a, 0x9c, 0x00, 0x8e, 0x9c, 0x00, 0x86, 0x9c,
+ 0x00, 0x81, 0x9c, 0x42, 0xe0, 0xd6, 0x49, 0x08,
+ 0x95, 0x49, 0x09, 0x87, 0x49, 0x17, 0x85, 0x46,
+ 0x00, 0xa9, 0x46, 0x00, 0x88, 0x46, 0x44, 0x85,
+ 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, 0x00,
+ 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, 0x1c,
+ 0x95, 0x37, 0x00, 0x88, 0x37, 0x9f, 0x76, 0x9e,
+ 0x60, 0x07, 0x88, 0x60, 0x2f, 0x92, 0x34, 0x00,
+ 0x81, 0x34, 0x04, 0x84, 0x34, 0x9b, 0x79, 0x02,
+ 0x80, 0x79, 0x99, 0x4d, 0x04, 0x80, 0x4d, 0x3f,
+ 0x9f, 0x59, 0x97, 0x58, 0x03, 0x93, 0x58, 0x01,
+ 0xad, 0x58, 0x83, 0x40, 0x00, 0x81, 0x40, 0x04,
+ 0x87, 0x40, 0x00, 0x82, 0x40, 0x00, 0x9c, 0x40,
+ 0x01, 0x82, 0x40, 0x03, 0x89, 0x40, 0x06, 0x88,
+ 0x40, 0x06, 0x9f, 0x6f, 0x9f, 0x6b, 0x1f, 0xa6,
+ 0x52, 0x03, 0x8b, 0x52, 0x08, 0xb5, 0x06, 0x02,
+ 0x86, 0x06, 0x95, 0x3a, 0x01, 0x87, 0x3a, 0x92,
+ 0x39, 0x04, 0x87, 0x39, 0x91, 0x7a, 0x06, 0x83,
+ 0x7a, 0x0b, 0x86, 0x7a, 0x4f, 0xc8, 0x70, 0x36,
+ 0xb2, 0x69, 0x0c, 0xb2, 0x69, 0x06, 0x85, 0x69,
+ 0xa7, 0x32, 0x07, 0x89, 0x32, 0x60, 0xc5, 0x9e,
+ 0x04, 0x00, 0xa9, 0x9f, 0x00, 0x82, 0x9f, 0x01,
+ 0x81, 0x9f, 0x4d, 0xa7, 0x6e, 0x07, 0xa9, 0x84,
+ 0x15, 0x99, 0x71, 0x25, 0x9b, 0x18, 0x13, 0x96,
+ 0x26, 0x08, 0xcd, 0x0e, 0x03, 0xa3, 0x0e, 0x08,
+ 0x80, 0x0e, 0xc2, 0x3c, 0x09, 0x80, 0x3c, 0x01,
+ 0x98, 0x85, 0x06, 0x89, 0x85, 0x05, 0xb4, 0x15,
+ 0x00, 0x91, 0x15, 0x07, 0xa6, 0x4f, 0x08, 0xdf,
+ 0x7f, 0x00, 0x93, 0x83, 0x0a, 0x91, 0x42, 0x00,
+ 0xab, 0x42, 0x40, 0x86, 0x5e, 0x00, 0x80, 0x5e,
+ 0x00, 0x83, 0x5e, 0x00, 0x8e, 0x5e, 0x00, 0x8a,
+ 0x5e, 0x05, 0xba, 0x44, 0x04, 0x89, 0x44, 0x05,
+ 0x83, 0x2b, 0x00, 0x87, 0x2b, 0x01, 0x81, 0x2b,
+ 0x01, 0x95, 0x2b, 0x00, 0x86, 0x2b, 0x00, 0x81,
+ 0x2b, 0x00, 0x84, 0x2b, 0x00, 0x80, 0x38, 0x88,
+ 0x2b, 0x01, 0x81, 0x2b, 0x01, 0x82, 0x2b, 0x01,
+ 0x80, 0x2b, 0x05, 0x80, 0x2b, 0x04, 0x86, 0x2b,
+ 0x01, 0x86, 0x2b, 0x02, 0x84, 0x2b, 0x60, 0x2a,
+ 0xdb, 0x63, 0x00, 0x84, 0x63, 0x1d, 0xc7, 0x97,
+ 0x07, 0x89, 0x97, 0x60, 0x45, 0xb5, 0x81, 0x01,
+ 0xa5, 0x81, 0x21, 0xc4, 0x5b, 0x0a, 0x89, 0x5b,
+ 0x05, 0x8c, 0x5c, 0x12, 0xb9, 0x8f, 0x05, 0x89,
+ 0x8f, 0x35, 0x9a, 0x02, 0x01, 0x8e, 0x02, 0x03,
+ 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, 0x60, 0x03,
+ 0xd2, 0x9e, 0x0b, 0x80, 0x9e, 0x86, 0x21, 0x01,
+ 0x80, 0x21, 0x01, 0x87, 0x21, 0x00, 0x81, 0x21,
+ 0x00, 0x9d, 0x21, 0x00, 0x81, 0x21, 0x01, 0x8b,
+ 0x21, 0x08, 0x89, 0x21, 0x45, 0x87, 0x61, 0x01,
+ 0xad, 0x61, 0x01, 0x8a, 0x61, 0x1a, 0xc7, 0xa1,
+ 0x07, 0xd2, 0x86, 0x0c, 0x8f, 0x12, 0xb8, 0x77,
+ 0x60, 0xa6, 0x88, 0x0c, 0x00, 0xac, 0x0c, 0x00,
+ 0x8d, 0x0c, 0x09, 0x9c, 0x0c, 0x02, 0x9f, 0x53,
+ 0x01, 0x95, 0x53, 0x00, 0x8d, 0x53, 0x48, 0x86,
+ 0x54, 0x00, 0x81, 0x54, 0x00, 0xab, 0x54, 0x02,
+ 0x80, 0x54, 0x00, 0x81, 0x54, 0x00, 0x88, 0x54,
+ 0x07, 0x89, 0x54, 0x05, 0x85, 0x2e, 0x00, 0x81,
+ 0x2e, 0x00, 0xa4, 0x2e, 0x00, 0x81, 0x2e, 0x00,
+ 0x85, 0x2e, 0x06, 0x89, 0x2e, 0x60, 0xd5, 0x98,
+ 0x4e, 0x60, 0x56, 0x80, 0x4b, 0x0e, 0xb1, 0x90,
+ 0x0c, 0x80, 0x90, 0xe3, 0x39, 0x1b, 0x60, 0x05,
+ 0xe0, 0x0e, 0x1b, 0x00, 0x84, 0x1b, 0x0a, 0xe0,
+ 0x63, 0x1b, 0x69, 0xeb, 0xe0, 0x02, 0x1e, 0x0c,
+ 0xe3, 0xce, 0x24, 0x00, 0x88, 0x24, 0x6f, 0x66,
+ 0xe1, 0xe6, 0x03, 0x70, 0x11, 0x58, 0xe1, 0xd8,
+ 0x08, 0x06, 0x9e, 0x5d, 0x00, 0x89, 0x5d, 0x03,
+ 0x81, 0x5d, 0xce, 0x98, 0x00, 0x89, 0x98, 0x05,
+ 0x9d, 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, 0x75,
+ 0x09, 0x89, 0x75, 0x00, 0x86, 0x75, 0x00, 0x94,
+ 0x75, 0x04, 0x92, 0x75, 0x62, 0x4f, 0xda, 0x55,
+ 0x60, 0x04, 0xca, 0x5a, 0x03, 0xb8, 0x5a, 0x06,
+ 0x90, 0x5a, 0x3f, 0x80, 0x91, 0x80, 0x65, 0x81,
+ 0x30, 0x80, 0x43, 0x0a, 0x81, 0x30, 0x0d, 0xf0,
+ 0x07, 0x97, 0x91, 0x07, 0xe2, 0x9f, 0x91, 0xe1,
+ 0x75, 0x43, 0x29, 0x88, 0x91, 0x70, 0x12, 0x86,
+ 0x83, 0x3e, 0x00, 0x86, 0x3e, 0x00, 0x81, 0x3e,
+ 0x00, 0x80, 0x3e, 0xe0, 0xbe, 0x36, 0x82, 0x3e,
+ 0x2c, 0x82, 0x36, 0x10, 0x83, 0x3e, 0x07, 0xe1,
+ 0x2b, 0x65, 0x68, 0xa3, 0xe0, 0x0a, 0x23, 0x04,
+ 0x8c, 0x23, 0x02, 0x88, 0x23, 0x06, 0x89, 0x23,
+ 0x01, 0x83, 0x23, 0x83, 0x19, 0x70, 0x01, 0xfb,
+ 0xad, 0x38, 0x01, 0x96, 0x38, 0x08, 0xe0, 0x13,
+ 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, 0xa6, 0x19,
+ 0x01, 0xbd, 0x19, 0x82, 0x38, 0x90, 0x19, 0x87,
+ 0x38, 0x81, 0x19, 0x86, 0x38, 0x9d, 0x19, 0x83,
+ 0x38, 0xbc, 0x19, 0x14, 0xc5, 0x2c, 0x60, 0x39,
+ 0x93, 0x19, 0x0b, 0xd6, 0x19, 0x08, 0x98, 0x19,
+ 0x60, 0x26, 0xd4, 0x19, 0x00, 0xc6, 0x19, 0x00,
+ 0x81, 0x19, 0x01, 0x80, 0x19, 0x01, 0x81, 0x19,
+ 0x01, 0x83, 0x19, 0x00, 0x8b, 0x19, 0x00, 0x80,
+ 0x19, 0x00, 0x86, 0x19, 0x00, 0xc0, 0x19, 0x00,
+ 0x83, 0x19, 0x01, 0x87, 0x19, 0x00, 0x86, 0x19,
+ 0x00, 0x9b, 0x19, 0x00, 0x83, 0x19, 0x00, 0x84,
+ 0x19, 0x00, 0x80, 0x19, 0x02, 0x86, 0x19, 0x00,
+ 0xe0, 0xf3, 0x19, 0x01, 0xe0, 0xc3, 0x19, 0x01,
+ 0xb1, 0x19, 0xe2, 0x2b, 0x82, 0x0e, 0x84, 0x82,
+ 0x00, 0x8e, 0x82, 0x63, 0xef, 0x9e, 0x46, 0x60,
+ 0x80, 0x86, 0x29, 0x00, 0x90, 0x29, 0x01, 0x86,
+ 0x29, 0x00, 0x81, 0x29, 0x00, 0x84, 0x29, 0x60,
+ 0x74, 0xac, 0x66, 0x02, 0x8d, 0x66, 0x01, 0x89,
+ 0x66, 0x03, 0x81, 0x66, 0x60, 0xdf, 0x9e, 0x99,
+ 0x10, 0xb9, 0x9d, 0x04, 0x80, 0x9d, 0x64, 0x7f,
+ 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, 0x27,
+ 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x57, 0x01,
+ 0x8f, 0x57, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01,
+ 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, 0x4b,
+ 0xbc, 0x19, 0x60, 0x61, 0x83, 0x04, 0x00, 0x9a,
+ 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, 0x01,
+ 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, 0x04,
+ 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, 0x80,
+ 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00,
+ 0x80, 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, 0x04,
+ 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, 0x80,
+ 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00,
+ 0x80, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04,
+ 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, 0x83,
+ 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, 0x00,
+ 0x89, 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, 0x04,
+ 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, 0x81,
+ 0x04, 0x60, 0xad, 0xab, 0x19, 0x03, 0xe0, 0x03,
+ 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, 0x00,
+ 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, 0x4d,
+ 0x19, 0x37, 0x99, 0x19, 0x80, 0x36, 0x81, 0x19,
+ 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, 0x81,
+ 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, 0x77,
+ 0x19, 0x04, 0x8f, 0x19, 0x02, 0x8c, 0x19, 0x02,
+ 0xe0, 0x13, 0x19, 0x0b, 0xd8, 0x19, 0x06, 0x8b,
+ 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, 0x03,
+ 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, 0x19,
+ 0x07, 0x9d, 0x19, 0x01, 0x81, 0x19, 0x4d, 0xe0,
+ 0xf3, 0x19, 0x0b, 0x8d, 0x19, 0x01, 0x84, 0x19,
+ 0x02, 0x84, 0x19, 0x02, 0x86, 0x19, 0x08, 0x9c,
+ 0x19, 0x02, 0x8a, 0x19, 0x04, 0x85, 0x19, 0x09,
+ 0x89, 0x19, 0x05, 0x87, 0x19, 0x07, 0x86, 0x19,
+ 0x08, 0xe0, 0x32, 0x19, 0x00, 0xb6, 0x19, 0x24,
+ 0x89, 0x19, 0x63, 0xa5, 0xf0, 0x96, 0x7f, 0x30,
+ 0x1f, 0xef, 0xd8, 0x30, 0x06, 0xe0, 0x7d, 0x30,
+ 0x01, 0xf0, 0x06, 0x21, 0x30, 0x0d, 0xf0, 0x0c,
+ 0xd0, 0x30, 0x6b, 0xbe, 0xe1, 0xbd, 0x30, 0x65,
+ 0x81, 0xf0, 0x02, 0xea, 0x30, 0x7a, 0xdc, 0x55,
+ 0x80, 0x19, 0x1d, 0xdf, 0x19, 0x60, 0x1f, 0xe0,
+ 0x8f, 0x38,
+};
+
+static const uint8_t unicode_script_ext_table[828] = {
+ 0x82, 0xc1, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x00,
+ 0x00, 0x01, 0x2c, 0x1c, 0x00, 0x0c, 0x01, 0x46,
+ 0x80, 0x92, 0x00, 0x00, 0x02, 0x1d, 0x6c, 0x00,
+ 0x02, 0x1d, 0x29, 0x01, 0x02, 0x1d, 0x46, 0x00,
+ 0x02, 0x1d, 0x29, 0x81, 0x03, 0x00, 0x00, 0x06,
+ 0x04, 0x64, 0x32, 0x89, 0x93, 0x9f, 0x0d, 0x00,
+ 0x00, 0x06, 0x04, 0x64, 0x32, 0x89, 0x93, 0x9f,
+ 0x00, 0x03, 0x04, 0x89, 0x93, 0x01, 0x00, 0x00,
+ 0x07, 0x01, 0x04, 0x64, 0x32, 0x89, 0x93, 0x9f,
+ 0x1f, 0x00, 0x00, 0x09, 0x01, 0x04, 0x51, 0x52,
+ 0x71, 0x7a, 0x32, 0x84, 0x89, 0x09, 0x00, 0x0a,
+ 0x02, 0x04, 0x89, 0x09, 0x00, 0x09, 0x03, 0x04,
+ 0x93, 0x9f, 0x05, 0x00, 0x00, 0x02, 0x04, 0x89,
+ 0x62, 0x00, 0x00, 0x02, 0x04, 0x32, 0x81, 0xfb,
+ 0x00, 0x00, 0x0d, 0x0b, 0x20, 0x2b, 0x2d, 0x2f,
+ 0x3d, 0x46, 0x50, 0x72, 0x7f, 0x90, 0x92, 0x97,
+ 0x00, 0x0c, 0x0b, 0x20, 0x2b, 0x2d, 0x2f, 0x3d,
+ 0x46, 0x50, 0x72, 0x90, 0x92, 0x97, 0x10, 0x00,
+ 0x00, 0x14, 0x0b, 0x20, 0x22, 0x2e, 0x54, 0x2b,
+ 0x2d, 0x2f, 0x3d, 0x4f, 0x50, 0x61, 0x72, 0x44,
+ 0x83, 0x88, 0x8f, 0x90, 0x92, 0x97, 0x00, 0x15,
+ 0x0b, 0x20, 0x22, 0x2e, 0x54, 0x2b, 0x2d, 0x2f,
+ 0x3d, 0x48, 0x4f, 0x50, 0x61, 0x72, 0x44, 0x83,
+ 0x88, 0x8f, 0x90, 0x92, 0x97, 0x09, 0x04, 0x20,
+ 0x22, 0x3c, 0x4f, 0x75, 0x00, 0x09, 0x03, 0x0b,
+ 0x15, 0x88, 0x75, 0x00, 0x09, 0x02, 0x2f, 0x5e,
+ 0x75, 0x00, 0x09, 0x02, 0x2d, 0x42, 0x80, 0x75,
+ 0x00, 0x0d, 0x02, 0x2b, 0x90, 0x80, 0x71, 0x00,
+ 0x09, 0x02, 0x3d, 0x61, 0x82, 0xcf, 0x00, 0x09,
+ 0x03, 0x15, 0x5f, 0x8c, 0x80, 0x30, 0x00, 0x00,
+ 0x02, 0x28, 0x46, 0x85, 0xb8, 0x00, 0x01, 0x04,
+ 0x11, 0x33, 0x8b, 0x8a, 0x80, 0x4a, 0x00, 0x01,
+ 0x02, 0x5c, 0x78, 0x00, 0x00, 0x00, 0x02, 0x5c,
+ 0x78, 0x84, 0x49, 0x00, 0x00, 0x04, 0x0b, 0x20,
+ 0x2b, 0x3d, 0x00, 0x01, 0x20, 0x00, 0x04, 0x0b,
+ 0x20, 0x2b, 0x3d, 0x00, 0x02, 0x20, 0x2b, 0x00,
+ 0x01, 0x20, 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02,
+ 0x20, 0x7f, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02,
+ 0x20, 0x7f, 0x00, 0x06, 0x20, 0x3d, 0x50, 0x72,
+ 0x90, 0x92, 0x00, 0x01, 0x20, 0x01, 0x02, 0x20,
+ 0x7f, 0x01, 0x01, 0x20, 0x00, 0x02, 0x20, 0x7f,
+ 0x00, 0x02, 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00,
+ 0x02, 0x20, 0x61, 0x00, 0x02, 0x0b, 0x20, 0x01,
+ 0x01, 0x20, 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01,
+ 0x20, 0x00, 0x08, 0x0b, 0x20, 0x2b, 0x3d, 0x61,
+ 0x72, 0x92, 0x97, 0x00, 0x02, 0x20, 0x2b, 0x00,
+ 0x03, 0x20, 0x2b, 0x3d, 0x01, 0x02, 0x0b, 0x20,
+ 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, 0x2b, 0x00,
+ 0x01, 0x61, 0x80, 0x44, 0x00, 0x01, 0x01, 0x2c,
+ 0x35, 0x00, 0x00, 0x02, 0x1d, 0x89, 0x00, 0x00,
+ 0x00, 0x01, 0x89, 0x81, 0xb3, 0x00, 0x00, 0x02,
+ 0x46, 0x5c, 0x80, 0x3f, 0x00, 0x00, 0x03, 0x20,
+ 0x2b, 0x46, 0x8c, 0xd1, 0x00, 0x00, 0x02, 0x1d,
+ 0x29, 0x81, 0x3c, 0x00, 0x01, 0x06, 0x0d, 0x31,
+ 0x30, 0x36, 0x3e, 0xa0, 0x00, 0x05, 0x0d, 0x31,
+ 0x30, 0x36, 0x3e, 0x01, 0x00, 0x00, 0x01, 0x30,
+ 0x00, 0x00, 0x09, 0x06, 0x0d, 0x31, 0x30, 0x36,
+ 0x3e, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x0d, 0x31,
+ 0x30, 0x36, 0x3e, 0x07, 0x06, 0x0d, 0x31, 0x30,
+ 0x36, 0x3e, 0xa0, 0x03, 0x05, 0x0d, 0x31, 0x30,
+ 0x36, 0x3e, 0x09, 0x00, 0x03, 0x02, 0x0d, 0x30,
+ 0x01, 0x00, 0x00, 0x05, 0x0d, 0x31, 0x30, 0x36,
+ 0x3e, 0x04, 0x02, 0x36, 0x3e, 0x00, 0x00, 0x00,
+ 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x03, 0x00,
+ 0x01, 0x03, 0x30, 0x36, 0x3e, 0x01, 0x01, 0x30,
+ 0x58, 0x00, 0x03, 0x02, 0x36, 0x3e, 0x02, 0x00,
+ 0x00, 0x02, 0x36, 0x3e, 0x59, 0x00, 0x00, 0x06,
+ 0x0d, 0x31, 0x30, 0x36, 0x3e, 0xa0, 0x00, 0x02,
+ 0x36, 0x3e, 0x80, 0x12, 0x00, 0x0f, 0x01, 0x30,
+ 0x1f, 0x00, 0x23, 0x01, 0x30, 0x3b, 0x00, 0x27,
+ 0x01, 0x30, 0x37, 0x00, 0x30, 0x01, 0x30, 0x0e,
+ 0x00, 0x0b, 0x01, 0x30, 0x32, 0x00, 0x00, 0x01,
+ 0x30, 0x57, 0x00, 0x18, 0x01, 0x30, 0x09, 0x00,
+ 0x04, 0x01, 0x30, 0x5f, 0x00, 0x1e, 0x01, 0x30,
+ 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, 0x29,
+ 0x80, 0x0f, 0x00, 0x07, 0x02, 0x30, 0x46, 0x80,
+ 0xa7, 0x00, 0x02, 0x0e, 0x20, 0x22, 0x2d, 0x2f,
+ 0x42, 0x3d, 0x3c, 0x4f, 0x50, 0x5b, 0x61, 0x44,
+ 0x8f, 0x97, 0x02, 0x0d, 0x20, 0x22, 0x2d, 0x2f,
+ 0x42, 0x3d, 0x3c, 0x4f, 0x5b, 0x61, 0x44, 0x8f,
+ 0x97, 0x03, 0x0b, 0x20, 0x22, 0x2d, 0x2f, 0x42,
+ 0x3c, 0x4f, 0x5b, 0x44, 0x8f, 0x97, 0x80, 0x36,
+ 0x00, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x00, 0x00,
+ 0x02, 0x20, 0x90, 0x39, 0x00, 0x00, 0x03, 0x3f,
+ 0x46, 0x5f, 0x80, 0x1f, 0x00, 0x00, 0x02, 0x10,
+ 0x3b, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, 0x04,
+ 0x64, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, 0x93,
+ 0x09, 0x00, 0x00, 0x02, 0x04, 0x93, 0x46, 0x00,
+ 0x01, 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x80,
+ 0x99, 0x00, 0x04, 0x06, 0x0d, 0x31, 0x30, 0x36,
+ 0x3e, 0xa0, 0x09, 0x00, 0x00, 0x02, 0x36, 0x3e,
+ 0x2c, 0x00, 0x01, 0x02, 0x36, 0x3e, 0x80, 0xdf,
+ 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4a, 0x00, 0x02,
+ 0x1c, 0x4a, 0x03, 0x00, 0x2c, 0x03, 0x1c, 0x49,
+ 0x4a, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4a, 0x81,
+ 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, 0x75,
+ 0x00, 0x00, 0x02, 0x52, 0x71, 0x87, 0x8d, 0x00,
+ 0x00, 0x02, 0x2b, 0x90, 0x00, 0x00, 0x00, 0x02,
+ 0x2b, 0x90, 0x36, 0x00, 0x01, 0x02, 0x2b, 0x90,
+ 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2b, 0x90, 0x00,
+ 0x00, 0x00, 0x02, 0x2b, 0x90, 0xc0, 0x5c, 0x4b,
+ 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, 0x11,
+ 0x01, 0x30, 0x9e, 0x5d, 0x00, 0x01, 0x01, 0x30,
+ 0xce, 0xcd, 0x2d, 0x00,
+};
+
+static const uint8_t unicode_prop_Hyphen_table[28] = {
+ 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52,
+ 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80,
+ 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40,
+ 0xa8, 0x80, 0xd6, 0x80,
+};
+
+static const uint8_t unicode_prop_Other_Math_table[200] = {
+ 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09,
+ 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80,
+ 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c,
+ 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00,
+ 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24,
+ 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b,
+ 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41,
+ 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80,
+ 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24,
+ 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97,
+ 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81,
+ 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95,
+ 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00,
+ 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4,
+ 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b,
+ 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f,
+ 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80,
+ 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e,
+ 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e,
+ 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x81,
+ 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08,
+ 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00,
+ 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00,
+ 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90,
+};
+
+static const uint8_t unicode_prop_Other_Alphabetic_table[417] = {
+ 0x43, 0x44, 0x80, 0x42, 0x69, 0x8d, 0x00, 0x01,
+ 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, 0x06, 0x8f,
+ 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, 0xa2, 0x80,
+ 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, 0x88, 0x02,
+ 0x03, 0x40, 0xa6, 0x8b, 0x16, 0x85, 0x93, 0xb5,
+ 0x09, 0x8e, 0x01, 0x22, 0x89, 0x81, 0x9c, 0x82,
+ 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81,
+ 0x9c, 0x82, 0xb9, 0x23, 0x09, 0x0b, 0x80, 0x9d,
+ 0x0a, 0x80, 0x8a, 0x82, 0xb9, 0x38, 0x10, 0x81,
+ 0x94, 0x81, 0x95, 0x13, 0x82, 0xb9, 0x31, 0x09,
+ 0x81, 0x88, 0x81, 0x89, 0x81, 0x9d, 0x80, 0xba,
+ 0x22, 0x10, 0x82, 0x89, 0x80, 0xa7, 0x83, 0xb9,
+ 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82,
+ 0xb9, 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9b,
+ 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, 0x80, 0x89,
+ 0x81, 0x9c, 0x82, 0xca, 0x28, 0x00, 0x87, 0x91,
+ 0x81, 0xbc, 0x01, 0x86, 0x91, 0x80, 0xe2, 0x01,
+ 0x28, 0x81, 0x8f, 0x80, 0x40, 0xa2, 0x90, 0x8a,
+ 0x8a, 0x80, 0xa3, 0xed, 0x8b, 0x00, 0x0b, 0x96,
+ 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x00,
+ 0x89, 0x83, 0x46, 0x73, 0x81, 0x9d, 0x81, 0x9d,
+ 0x81, 0x9d, 0x81, 0xc1, 0x92, 0x40, 0xbb, 0x81,
+ 0xa1, 0x80, 0xf5, 0x8b, 0x83, 0x88, 0x40, 0xdd,
+ 0x84, 0xb8, 0x89, 0x81, 0x93, 0xc9, 0x81, 0x8a,
+ 0x82, 0xb0, 0x84, 0xaf, 0x8e, 0xbb, 0x82, 0x9d,
+ 0x88, 0x09, 0xb8, 0x8a, 0xb1, 0x92, 0x41, 0xaf,
+ 0x8d, 0x46, 0xc0, 0xb3, 0x48, 0xf5, 0x9f, 0x60,
+ 0x78, 0x73, 0x87, 0xa1, 0x81, 0x41, 0x61, 0x07,
+ 0x80, 0x96, 0x84, 0xd7, 0x81, 0xb1, 0x8f, 0x00,
+ 0xb8, 0x80, 0xa5, 0x84, 0x9b, 0x8b, 0xac, 0x83,
+ 0xaf, 0x8b, 0xa4, 0x80, 0xc2, 0x8d, 0x8b, 0x07,
+ 0x81, 0xac, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x80,
+ 0xab, 0x24, 0x80, 0x40, 0xec, 0x87, 0x60, 0x4f,
+ 0x32, 0x80, 0x48, 0x56, 0x84, 0x46, 0x85, 0x10,
+ 0x0c, 0x83, 0x43, 0x13, 0x83, 0x41, 0x82, 0x81,
+ 0x41, 0x52, 0x82, 0xb4, 0x8d, 0xac, 0x81, 0x8c,
+ 0x80, 0xac, 0x88, 0x88, 0x80, 0xbc, 0x82, 0xa3,
+ 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, 0x8c, 0x8d,
+ 0x81, 0xdb, 0x88, 0x08, 0x28, 0x40, 0x9f, 0x89,
+ 0x96, 0x83, 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80,
+ 0x89, 0x81, 0x40, 0xd0, 0x8c, 0x02, 0xe9, 0x91,
+ 0x40, 0xec, 0x31, 0x86, 0x9c, 0x81, 0xd1, 0x8e,
+ 0x00, 0xe9, 0x8a, 0xe6, 0x8d, 0x41, 0x00, 0x8c,
+ 0x40, 0xf6, 0x28, 0x09, 0x0a, 0x00, 0x80, 0x40,
+ 0x8d, 0x31, 0x2b, 0x80, 0x9b, 0x89, 0xa9, 0x20,
+ 0x83, 0x91, 0x8a, 0xad, 0x8d, 0x41, 0x96, 0x38,
+ 0x86, 0xd2, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00,
+ 0x08, 0x10, 0x02, 0x80, 0xc1, 0x20, 0x08, 0x83,
+ 0x41, 0x5b, 0x83, 0x60, 0x50, 0x57, 0x00, 0xb6,
+ 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, 0x80, 0x60,
+ 0x23, 0x60, 0x30, 0x90, 0x0e, 0x01, 0x04, 0x49,
+ 0x1b, 0x80, 0x47, 0xe7, 0x99, 0x85, 0x99, 0x85,
+ 0x99,
+};
+
+static const uint8_t unicode_prop_Other_Lowercase_table[59] = {
+ 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88,
+ 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x59,
+ 0xb0, 0xbe, 0x8c, 0x80, 0xa1, 0xa4, 0x42, 0xb0,
+ 0x80, 0x8c, 0x80, 0x8f, 0x8c, 0x40, 0xd2, 0x8f,
+ 0x43, 0x4f, 0x99, 0x47, 0x91, 0x81, 0x60, 0x7a,
+ 0x1d, 0x81, 0x40, 0xd1, 0x80, 0x40, 0x86, 0x81,
+ 0x43, 0x61, 0x83, 0x60, 0x5c, 0x1f, 0x01, 0x10,
+ 0xa9, 0x80, 0x88,
+};
+
+static const uint8_t unicode_prop_Other_Uppercase_table[15] = {
+ 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61,
+ 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99,
+};
+
+static const uint8_t unicode_prop_Other_Grapheme_Extend_table[65] = {
+ 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80,
+ 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe9,
+ 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, 0x80, 0xf6,
+ 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5,
+ 0x80, 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81,
+ 0x53, 0x9d, 0x80, 0x97, 0x80, 0x41, 0x57, 0x80,
+ 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x43, 0x7f, 0x80,
+ 0x60, 0xb8, 0x33, 0x07, 0x84, 0x6c, 0x2e, 0xac,
+ 0xdf,
+};
+
+static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = {
+ 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52,
+ 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60,
+ 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06,
+ 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f,
+};
+
+static const uint8_t unicode_prop_Other_ID_Start_table[11] = {
+ 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80,
+ 0x4f, 0x6b, 0x81,
+};
+
+static const uint8_t unicode_prop_Other_ID_Continue_table[12] = {
+ 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0,
+ 0x88, 0x46, 0x67, 0x80,
+};
+
+static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[19] = {
+ 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80,
+ 0x41, 0x7f, 0x81, 0xcf, 0x80, 0x61, 0x07, 0xd9,
+ 0x80, 0x8e, 0x80,
+};
+
+static const uint8_t unicode_prop_XID_Start1_table[31] = {
+ 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80,
+ 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85,
+ 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81,
+};
+
+static const uint8_t unicode_prop_XID_Continue1_table[23] = {
+ 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60,
+ 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+};
+
+static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = {
+ 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e,
+ 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87,
+ 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80,
+};
+
+static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = {
+ 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44,
+ 0x3c, 0x80, 0x59, 0x11, 0x80, 0x40, 0xe4, 0x3f,
+ 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, 0x11, 0x80,
+ 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b,
+ 0x84,
+};
+
+static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[448] = {
+ 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12,
+ 0x10, 0x82, 0x9f, 0x80, 0xcf, 0x01, 0x80, 0x8b,
+ 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80,
+ 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08,
+ 0x81, 0x89, 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08,
+ 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80,
+ 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87,
+ 0xfb, 0x08, 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11,
+ 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe,
+ 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88,
+ 0x03, 0x03, 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00,
+ 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03,
+ 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81,
+ 0x46, 0x52, 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10,
+ 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1,
+ 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x3f, 0x87,
+ 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80,
+ 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08,
+ 0x8f, 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b,
+ 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a,
+ 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a,
+ 0x01, 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06,
+ 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80,
+ 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41,
+ 0x34, 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6,
+ 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0,
+ 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40,
+ 0xd5, 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09,
+ 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf,
+ 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f,
+ 0x60, 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40,
+ 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80,
+ 0x60, 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81,
+ 0x89, 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9,
+ 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x97, 0x04, 0x00,
+ 0x01, 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, 0x91,
+ 0xbf, 0x81, 0xb5, 0xa7, 0x8c, 0x82, 0x99, 0x95,
+ 0x94, 0x81, 0x8b, 0x80, 0x92, 0x03, 0x1a, 0x00,
+ 0x80, 0x40, 0x86, 0x08, 0x80, 0x9f, 0x99, 0x40,
+ 0x83, 0x15, 0x0d, 0x0d, 0x0a, 0x16, 0x06, 0x80,
+ 0x88, 0x47, 0x87, 0x20, 0xa9, 0x80, 0x88, 0x60,
+ 0xb4, 0xe4, 0x83, 0x54, 0xb9, 0x86, 0x8d, 0x87,
+ 0xbf, 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01,
+ 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80,
+ 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04,
+ 0x00, 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23,
+ 0x81, 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00,
+ 0x08, 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18,
+ 0x00, 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03,
+ 0x00, 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80,
+ 0x90, 0x42, 0x43, 0x8a, 0x84, 0x9e, 0x80, 0x9f,
+ 0x99, 0x82, 0xa2, 0x80, 0xee, 0x82, 0x8c, 0xab,
+ 0x83, 0x88, 0x31, 0x49, 0x9d, 0x89, 0x60, 0xfc,
+ 0x05, 0x42, 0x1d, 0x6b, 0x05, 0xe1, 0x4f, 0xff,
+};
+
+static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = {
+ 0xaf, 0x89, 0x35, 0x99, 0x85,
+};
+
+static const uint8_t unicode_prop_Bidi_Control_table[10] = {
+ 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84,
+ 0xb6, 0x83,
+};
+
+static const uint8_t unicode_prop_Dash_table[55] = {
+ 0xac, 0x80, 0x45, 0x5b, 0x80, 0xb2, 0x80, 0x4e,
+ 0x40, 0x80, 0x44, 0x04, 0x80, 0x48, 0x08, 0x85,
+ 0xbc, 0x80, 0xa6, 0x80, 0x8e, 0x80, 0x41, 0x85,
+ 0x80, 0x4c, 0x03, 0x01, 0x80, 0x9e, 0x0b, 0x80,
+ 0x9b, 0x80, 0x41, 0xbd, 0x80, 0x92, 0x80, 0xee,
+ 0x80, 0x60, 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89,
+ 0x80, 0x40, 0xa8, 0x80, 0x4f, 0x9e, 0x80,
+};
+
+static const uint8_t unicode_prop_Deprecated_table[23] = {
+ 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02,
+ 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85,
+ 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80,
+};
+
+static const uint8_t unicode_prop_Diacritic_table[391] = {
+ 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81,
+ 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b,
+ 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0,
+ 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00,
+ 0x40, 0x85, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a,
+ 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1,
+ 0x81, 0xfd, 0x87, 0xa8, 0x89, 0x8f, 0x9b, 0xbc,
+ 0x80, 0x8f, 0x02, 0x83, 0x9b, 0x80, 0xc9, 0x80,
+ 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80,
+ 0x8f, 0x80, 0xae, 0x82, 0xbb, 0x80, 0x8f, 0x06,
+ 0x80, 0xf6, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed,
+ 0x80, 0x8f, 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb,
+ 0x80, 0xfb, 0x28, 0x80, 0xea, 0x80, 0x8c, 0x84,
+ 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, 0x81, 0xc1,
+ 0x10, 0x81, 0xbd, 0x80, 0xef, 0x00, 0x81, 0xa7,
+ 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, 0x81, 0x42,
+ 0xc0, 0x82, 0x43, 0xb3, 0x81, 0x40, 0xb2, 0x8a,
+ 0x88, 0x80, 0x41, 0x5a, 0x82, 0x41, 0x38, 0x39,
+ 0x80, 0xaf, 0x8e, 0x81, 0x8a, 0xe7, 0x80, 0x8e,
+ 0x80, 0xa5, 0x88, 0xb5, 0x81, 0x40, 0x89, 0x81,
+ 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, 0xb1,
+ 0xbe, 0xd8, 0x8b, 0xa4, 0x8a, 0x41, 0xbc, 0x00,
+ 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, 0x82, 0x8c,
+ 0x81, 0x4c, 0xef, 0x82, 0x41, 0x3c, 0x80, 0x41,
+ 0xf9, 0x85, 0xe8, 0x83, 0xde, 0x80, 0x60, 0x75,
+ 0x71, 0x80, 0x8b, 0x08, 0x80, 0x9b, 0x81, 0xd1,
+ 0x81, 0x8d, 0xa1, 0xe5, 0x82, 0xec, 0x81, 0x40,
+ 0xc9, 0x80, 0x9a, 0x91, 0xb8, 0x83, 0xa3, 0x80,
+ 0xde, 0x80, 0x8b, 0x80, 0xa3, 0x80, 0x40, 0x94,
+ 0x82, 0xc0, 0x83, 0xb2, 0x80, 0xe3, 0x84, 0x88,
+ 0x82, 0xff, 0x81, 0x60, 0x4f, 0x2f, 0x80, 0x43,
+ 0x00, 0x8f, 0x41, 0x0d, 0x00, 0x80, 0xae, 0x80,
+ 0xac, 0x81, 0xc2, 0x80, 0x42, 0xfb, 0x80, 0x44,
+ 0x9e, 0x28, 0xa9, 0x80, 0x88, 0x43, 0x29, 0x81,
+ 0x42, 0x3a, 0x85, 0x42, 0x1d, 0x8a, 0xb0, 0x83,
+ 0x40, 0xbf, 0x80, 0xa8, 0x80, 0xc7, 0x81, 0xf7,
+ 0x81, 0xbd, 0x80, 0xcb, 0x80, 0x88, 0x82, 0xe7,
+ 0x81, 0x40, 0xb1, 0x81, 0xd0, 0x80, 0x8f, 0x80,
+ 0x97, 0x32, 0x84, 0x40, 0xcc, 0x02, 0x80, 0xfa,
+ 0x81, 0x40, 0xfa, 0x81, 0xfd, 0x80, 0xf5, 0x81,
+ 0xf2, 0x80, 0x41, 0x0c, 0x81, 0x41, 0x01, 0x0b,
+ 0x80, 0x40, 0x9b, 0x80, 0xd2, 0x80, 0x91, 0x80,
+ 0xd0, 0x80, 0x41, 0xa4, 0x80, 0x41, 0x01, 0x00,
+ 0x81, 0xd0, 0x80, 0x60, 0x4d, 0x57, 0x84, 0xba,
+ 0x86, 0x44, 0x57, 0x90, 0xcf, 0x81, 0x60, 0x3f,
+ 0xfd, 0x18, 0x30, 0x81, 0x5f, 0x00, 0xad, 0x81,
+ 0x96, 0x42, 0x1f, 0x12, 0x2f, 0x39, 0x86, 0x9d,
+ 0x83, 0x4f, 0x81, 0x86, 0x41, 0x76, 0x80, 0xbc,
+ 0x83, 0x45, 0xdf, 0x86, 0xec, 0x10, 0x82,
+};
+
+static const uint8_t unicode_prop_Extender_table[92] = {
+ 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d,
+ 0x80, 0x41, 0xb8, 0x80, 0x43, 0x59, 0x80, 0x42,
+ 0xef, 0x80, 0xfe, 0x80, 0x49, 0x42, 0x80, 0xb7,
+ 0x80, 0x42, 0x62, 0x80, 0x41, 0x8d, 0x80, 0xc3,
+ 0x80, 0x53, 0x88, 0x80, 0xaa, 0x84, 0xe6, 0x81,
+ 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x45, 0xf5,
+ 0x80, 0x43, 0xc1, 0x80, 0x95, 0x80, 0x40, 0x88,
+ 0x80, 0xeb, 0x80, 0x94, 0x81, 0x60, 0x54, 0x7a,
+ 0x80, 0x48, 0x0f, 0x81, 0x4b, 0xd9, 0x80, 0x42,
+ 0x67, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8,
+ 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57,
+ 0x81, 0x48, 0x05, 0x82,
+};
+
+static const uint8_t unicode_prop_Hex_Digit_table[12] = {
+ 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8,
+ 0x89, 0x35, 0x99, 0x85,
+};
+
+static const uint8_t unicode_prop_IDS_Binary_Operator_table[5] = {
+ 0x60, 0x2f, 0xef, 0x09, 0x87,
+};
+
+static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = {
+ 0x60, 0x2f, 0xf1, 0x81,
+};
+
+static const uint8_t unicode_prop_Ideographic_table[66] = {
+ 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82,
+ 0x43, 0xc4, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xff,
+ 0x60, 0x58, 0xff, 0x41, 0x6d, 0x81, 0xe9, 0x60,
+ 0x75, 0x09, 0x80, 0x9a, 0x57, 0xf7, 0x87, 0x44,
+ 0xd5, 0xa9, 0x88, 0x60, 0x24, 0x66, 0x41, 0x8b,
+ 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, 0x50,
+ 0x38, 0x86, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d,
+ 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1,
+ 0x53, 0x4a,
+};
+
+static const uint8_t unicode_prop_Join_Control_table[4] = {
+ 0x60, 0x20, 0x0b, 0x81,
+};
+
+static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = {
+ 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11,
+ 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81,
+};
+
+static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = {
+ 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81,
+};
+
+static const uint8_t unicode_prop_Pattern_Syntax_table[58] = {
+ 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99,
+ 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03,
+ 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17,
+ 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41,
+ 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d,
+ 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13,
+ 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41,
+ 0x04, 0x81,
+};
+
+static const uint8_t unicode_prop_Pattern_White_Space_table[11] = {
+ 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87,
+ 0x81, 0x97, 0x81,
+};
+
+static const uint8_t unicode_prop_Quotation_Mark_table[31] = {
+ 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80,
+ 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80,
+ 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20,
+ 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81,
+};
+
+static const uint8_t unicode_prop_Radical_table[9] = {
+ 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40,
+ 0xd5,
+};
+
+static const uint8_t unicode_prop_Regional_Indicator_table[4] = {
+ 0x61, 0xf1, 0xe5, 0x99,
+};
+
+static const uint8_t unicode_prop_Sentence_Terminal_table[194] = {
+ 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48,
+ 0x80, 0x40, 0x92, 0x82, 0x40, 0xb3, 0x80, 0xaa,
+ 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81,
+ 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15,
+ 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81,
+ 0x40, 0xcb, 0x04, 0x80, 0x41, 0x39, 0x81, 0x41,
+ 0x61, 0x83, 0x40, 0xad, 0x09, 0x81, 0x9c, 0x81,
+ 0x40, 0xbb, 0x81, 0xc0, 0x81, 0x43, 0xbb, 0x81,
+ 0x88, 0x82, 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x95,
+ 0x81, 0x41, 0xac, 0x80, 0x60, 0x74, 0xfb, 0x80,
+ 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, 0x41,
+ 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x97,
+ 0x81, 0x40, 0x92, 0x82, 0x40, 0x8f, 0x81, 0x40,
+ 0xf8, 0x80, 0x60, 0x52, 0x65, 0x02, 0x81, 0x40,
+ 0xa8, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0xc0, 0x80,
+ 0x4a, 0xf3, 0x81, 0x44, 0xfc, 0x84, 0xab, 0x83,
+ 0x40, 0xbc, 0x81, 0xf4, 0x83, 0xfe, 0x82, 0x40,
+ 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x08, 0x81,
+ 0xeb, 0x80, 0x41, 0xa0, 0x81, 0x41, 0x74, 0x0c,
+ 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04,
+ 0x00, 0x80, 0x40, 0xfa, 0x81, 0xd6, 0x81, 0x41,
+ 0xa3, 0x81, 0x42, 0xb3, 0x81, 0x60, 0x4b, 0x74,
+ 0x81, 0x40, 0x84, 0x80, 0xc0, 0x81, 0x8a, 0x80,
+ 0x43, 0x52, 0x80, 0x60, 0x4e, 0x05, 0x80, 0x5d,
+ 0xe7, 0x80,
+};
+
+static const uint8_t unicode_prop_Soft_Dotted_table[74] = {
+ 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80,
+ 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f,
+ 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2,
+ 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40,
+ 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81,
+ 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1,
+ 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1,
+ 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1,
+ 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0x48,
+ 0x85, 0x80,
+};
+
+static const uint8_t unicode_prop_Terminal_Punctuation_table[246] = {
+ 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80,
+ 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8,
+ 0x80, 0xc7, 0x80, 0x8d, 0x00, 0x82, 0x40, 0xb3,
+ 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5,
+ 0x8e, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, 0xf3,
+ 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, 0x81,
+ 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, 0x82,
+ 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, 0x19,
+ 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, 0x40,
+ 0xad, 0x08, 0x82, 0x9c, 0x81, 0x40, 0xbb, 0x84,
+ 0xbd, 0x81, 0x43, 0xbb, 0x81, 0x88, 0x82, 0x4d,
+ 0xe3, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x0a,
+ 0x81, 0x41, 0xab, 0x81, 0x60, 0x74, 0xfa, 0x81,
+ 0x41, 0x0c, 0x82, 0x40, 0xe2, 0x84, 0x41, 0x7d,
+ 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x96, 0x82,
+ 0x40, 0x92, 0x82, 0xfe, 0x80, 0x8f, 0x81, 0x40,
+ 0xf8, 0x80, 0x60, 0x52, 0x63, 0x10, 0x83, 0x40,
+ 0xa8, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80,
+ 0xc0, 0x01, 0x80, 0x44, 0x39, 0x80, 0xaf, 0x80,
+ 0x44, 0x85, 0x80, 0x40, 0xc6, 0x80, 0x41, 0x35,
+ 0x81, 0x40, 0x97, 0x85, 0xc3, 0x85, 0xd8, 0x83,
+ 0x43, 0xb7, 0x84, 0xab, 0x83, 0x40, 0xbc, 0x86,
+ 0xef, 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80,
+ 0x8f, 0x81, 0xd7, 0x84, 0xeb, 0x80, 0x41, 0xa0,
+ 0x82, 0x8b, 0x81, 0x41, 0x65, 0x1a, 0x8e, 0xe8,
+ 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, 0x00, 0x80,
+ 0x40, 0xfa, 0x81, 0xd6, 0x0b, 0x81, 0x41, 0x9d,
+ 0x82, 0xac, 0x80, 0x42, 0x84, 0x81, 0x45, 0x76,
+ 0x84, 0x60, 0x45, 0xf8, 0x81, 0x40, 0x84, 0x80,
+ 0xc0, 0x82, 0x89, 0x80, 0x43, 0x51, 0x81, 0x60,
+ 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83,
+};
+
+static const uint8_t unicode_prop_Unified_Ideograph_table[42] = {
+ 0x60, 0x33, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x51,
+ 0xff, 0x60, 0x5a, 0x0d, 0x08, 0x00, 0x81, 0x89,
+ 0x00, 0x00, 0x09, 0x82, 0x61, 0x05, 0xd5, 0x60,
+ 0xa6, 0xdf, 0x9f, 0x50, 0x38, 0x86, 0x40, 0xdd,
+ 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x54, 0x1e,
+ 0x53, 0x4a,
+};
+
+static const uint8_t unicode_prop_Variation_Selector_table[13] = {
+ 0x58, 0x0a, 0x10, 0x80, 0x60, 0xe5, 0xef, 0x8f,
+ 0x6d, 0x02, 0xef, 0x40, 0xef,
+};
+
+static const uint8_t unicode_prop_White_Space_table[22] = {
+ 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80,
+ 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c,
+ 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80,
+};
+
+static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = {
+ 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80,
+ 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e,
+ 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81,
+ 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0,
+ 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18,
+ 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23,
+ 0x88, 0x08, 0x00, 0x39, 0x9e, 0x0b, 0x20, 0x88,
+ 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81,
+ 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d,
+ 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d,
+ 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35,
+ 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89,
+ 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08,
+ 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18,
+ 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00,
+ 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80,
+ 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89,
+ 0xaa, 0x87, 0x41, 0xaa, 0x89, 0x0f, 0x60, 0xce,
+ 0x3c, 0x2c, 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00,
+ 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08,
+ 0x81, 0x60, 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8,
+ 0x80, 0xb8, 0x80, 0xb8, 0x80,
+};
+
+static const uint8_t unicode_prop_Emoji_table[239] = {
+ 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f,
+ 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95,
+ 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81,
+ 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a,
+ 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80,
+ 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01,
+ 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f,
+ 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08,
+ 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a,
+ 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04,
+ 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03,
+ 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02,
+ 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00,
+ 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88,
+ 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81,
+ 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80,
+ 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00,
+ 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80,
+ 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89,
+ 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88,
+ 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09,
+ 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80,
+ 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01,
+ 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88,
+ 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05,
+ 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 0x84,
+ 0x88, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2,
+ 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80,
+ 0x40, 0xb8, 0xef, 0x22, 0x22, 0x86, 0x88, 0x9c,
+ 0x82, 0x8a, 0x25, 0x89, 0x89, 0x2f, 0x3e,
+};
+
+static const uint8_t unicode_prop_Emoji_Component_table[28] = {
+ 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40,
+ 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3,
+ 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83,
+ 0x6c, 0x06, 0x6b, 0xdf,
+};
+
+static const uint8_t unicode_prop_Emoji_Modifier_table[4] = {
+ 0x61, 0xf3, 0xfa, 0x84,
+};
+
+static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = {
+ 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f,
+ 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01,
+ 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x92, 0x10, 0x1a,
+ 0x02, 0x30, 0x00, 0x97, 0x80, 0x40, 0xc8, 0x0b,
+ 0x80, 0x94, 0x03, 0x81, 0x40, 0xad, 0x12, 0x84,
+ 0xd2, 0x80, 0x8f, 0x82, 0x88, 0x80, 0x8a, 0x80,
+ 0x42, 0x3e, 0x01, 0x07, 0x3d, 0x80, 0x88, 0x89,
+ 0x0a, 0xb7, 0x80, 0xbc, 0x08, 0x08, 0x80, 0x90,
+ 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x86,
+};
+
+static const uint8_t unicode_prop_Emoji_Presentation_table[145] = {
+ 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01,
+ 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b,
+ 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90,
+ 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03,
+ 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00,
+ 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d,
+ 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61,
+ 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd,
+ 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93,
+ 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0,
+ 0x8b, 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa,
+ 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80,
+ 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91,
+ 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf,
+ 0xc5, 0x28, 0x12, 0x0a, 0x22, 0x8a, 0x0e, 0x88,
+ 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80,
+ 0x89, 0x80, 0x40, 0xb8, 0xef, 0x22, 0x22, 0x86,
+ 0x88, 0x9c, 0x82, 0x8a, 0x25, 0x89, 0x89, 0x2f,
+ 0x3e,
+};
+
+static const uint8_t unicode_prop_Extended_Pictographic_table[156] = {
+ 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b,
+ 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85,
+ 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde,
+ 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6,
+ 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80,
+ 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5,
+ 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89,
+ 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80,
+ 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80,
+ 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82,
+ 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80,
+ 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd,
+ 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb,
+ 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8,
+ 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03,
+ 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41,
+ 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b,
+ 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d,
+ 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8,
+ 0x40, 0xff, 0x43, 0xfd,
+};
+
+static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = {
+ 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb,
+ 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4,
+ 0x84, 0x47, 0xfa, 0x84, 0x99, 0x84, 0xb0, 0x8f,
+ 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40,
+ 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60,
+ 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e,
+ 0x84, 0x4f, 0xff,
+};
+
+typedef enum {
+ UNICODE_PROP_Hyphen,
+ UNICODE_PROP_Other_Math,
+ UNICODE_PROP_Other_Alphabetic,
+ UNICODE_PROP_Other_Lowercase,
+ UNICODE_PROP_Other_Uppercase,
+ UNICODE_PROP_Other_Grapheme_Extend,
+ UNICODE_PROP_Other_Default_Ignorable_Code_Point,
+ UNICODE_PROP_Other_ID_Start,
+ UNICODE_PROP_Other_ID_Continue,
+ UNICODE_PROP_Prepended_Concatenation_Mark,
+ UNICODE_PROP_ID_Continue1,
+ UNICODE_PROP_XID_Start1,
+ UNICODE_PROP_XID_Continue1,
+ UNICODE_PROP_Changes_When_Titlecased1,
+ UNICODE_PROP_Changes_When_Casefolded1,
+ UNICODE_PROP_Changes_When_NFKC_Casefolded1,
+ UNICODE_PROP_ASCII_Hex_Digit,
+ UNICODE_PROP_Bidi_Control,
+ UNICODE_PROP_Dash,
+ UNICODE_PROP_Deprecated,
+ UNICODE_PROP_Diacritic,
+ UNICODE_PROP_Extender,
+ UNICODE_PROP_Hex_Digit,
+ UNICODE_PROP_IDS_Binary_Operator,
+ UNICODE_PROP_IDS_Trinary_Operator,
+ UNICODE_PROP_Ideographic,
+ UNICODE_PROP_Join_Control,
+ UNICODE_PROP_Logical_Order_Exception,
+ UNICODE_PROP_Noncharacter_Code_Point,
+ UNICODE_PROP_Pattern_Syntax,
+ UNICODE_PROP_Pattern_White_Space,
+ UNICODE_PROP_Quotation_Mark,
+ UNICODE_PROP_Radical,
+ UNICODE_PROP_Regional_Indicator,
+ UNICODE_PROP_Sentence_Terminal,
+ UNICODE_PROP_Soft_Dotted,
+ UNICODE_PROP_Terminal_Punctuation,
+ UNICODE_PROP_Unified_Ideograph,
+ UNICODE_PROP_Variation_Selector,
+ UNICODE_PROP_White_Space,
+ UNICODE_PROP_Bidi_Mirrored,
+ UNICODE_PROP_Emoji,
+ UNICODE_PROP_Emoji_Component,
+ UNICODE_PROP_Emoji_Modifier,
+ UNICODE_PROP_Emoji_Modifier_Base,
+ UNICODE_PROP_Emoji_Presentation,
+ UNICODE_PROP_Extended_Pictographic,
+ UNICODE_PROP_Default_Ignorable_Code_Point,
+ UNICODE_PROP_ID_Start,
+ UNICODE_PROP_Case_Ignorable,
+ UNICODE_PROP_ASCII,
+ UNICODE_PROP_Alphabetic,
+ UNICODE_PROP_Any,
+ UNICODE_PROP_Assigned,
+ UNICODE_PROP_Cased,
+ UNICODE_PROP_Changes_When_Casefolded,
+ UNICODE_PROP_Changes_When_Casemapped,
+ UNICODE_PROP_Changes_When_Lowercased,
+ UNICODE_PROP_Changes_When_NFKC_Casefolded,
+ UNICODE_PROP_Changes_When_Titlecased,
+ UNICODE_PROP_Changes_When_Uppercased,
+ UNICODE_PROP_Grapheme_Base,
+ UNICODE_PROP_Grapheme_Extend,
+ UNICODE_PROP_ID_Continue,
+ UNICODE_PROP_Lowercase,
+ UNICODE_PROP_Math,
+ UNICODE_PROP_Uppercase,
+ UNICODE_PROP_XID_Continue,
+ UNICODE_PROP_XID_Start,
+ UNICODE_PROP_Cased1,
+ UNICODE_PROP_COUNT,
+} UnicodePropertyEnum;
+
+static const char unicode_prop_name_table[] =
+ "ASCII_Hex_Digit,AHex" "\0"
+ "Bidi_Control,Bidi_C" "\0"
+ "Dash" "\0"
+ "Deprecated,Dep" "\0"
+ "Diacritic,Dia" "\0"
+ "Extender,Ext" "\0"
+ "Hex_Digit,Hex" "\0"
+ "IDS_Binary_Operator,IDSB" "\0"
+ "IDS_Trinary_Operator,IDST" "\0"
+ "Ideographic,Ideo" "\0"
+ "Join_Control,Join_C" "\0"
+ "Logical_Order_Exception,LOE" "\0"
+ "Noncharacter_Code_Point,NChar" "\0"
+ "Pattern_Syntax,Pat_Syn" "\0"
+ "Pattern_White_Space,Pat_WS" "\0"
+ "Quotation_Mark,QMark" "\0"
+ "Radical" "\0"
+ "Regional_Indicator,RI" "\0"
+ "Sentence_Terminal,STerm" "\0"
+ "Soft_Dotted,SD" "\0"
+ "Terminal_Punctuation,Term" "\0"
+ "Unified_Ideograph,UIdeo" "\0"
+ "Variation_Selector,VS" "\0"
+ "White_Space,space" "\0"
+ "Bidi_Mirrored,Bidi_M" "\0"
+ "Emoji" "\0"
+ "Emoji_Component,EComp" "\0"
+ "Emoji_Modifier,EMod" "\0"
+ "Emoji_Modifier_Base,EBase" "\0"
+ "Emoji_Presentation,EPres" "\0"
+ "Extended_Pictographic,ExtPict" "\0"
+ "Default_Ignorable_Code_Point,DI" "\0"
+ "ID_Start,IDS" "\0"
+ "Case_Ignorable,CI" "\0"
+ "ASCII" "\0"
+ "Alphabetic,Alpha" "\0"
+ "Any" "\0"
+ "Assigned" "\0"
+ "Cased" "\0"
+ "Changes_When_Casefolded,CWCF" "\0"
+ "Changes_When_Casemapped,CWCM" "\0"
+ "Changes_When_Lowercased,CWL" "\0"
+ "Changes_When_NFKC_Casefolded,CWKCF" "\0"
+ "Changes_When_Titlecased,CWT" "\0"
+ "Changes_When_Uppercased,CWU" "\0"
+ "Grapheme_Base,Gr_Base" "\0"
+ "Grapheme_Extend,Gr_Ext" "\0"
+ "ID_Continue,IDC" "\0"
+ "Lowercase,Lower" "\0"
+ "Math" "\0"
+ "Uppercase,Upper" "\0"
+ "XID_Continue,XIDC" "\0"
+ "XID_Start,XIDS" "\0"
+;
+
+static const uint8_t * const unicode_prop_table[] = {
+ unicode_prop_Hyphen_table,
+ unicode_prop_Other_Math_table,
+ unicode_prop_Other_Alphabetic_table,
+ unicode_prop_Other_Lowercase_table,
+ unicode_prop_Other_Uppercase_table,
+ unicode_prop_Other_Grapheme_Extend_table,
+ unicode_prop_Other_Default_Ignorable_Code_Point_table,
+ unicode_prop_Other_ID_Start_table,
+ unicode_prop_Other_ID_Continue_table,
+ unicode_prop_Prepended_Concatenation_Mark_table,
+ unicode_prop_ID_Continue1_table,
+ unicode_prop_XID_Start1_table,
+ unicode_prop_XID_Continue1_table,
+ unicode_prop_Changes_When_Titlecased1_table,
+ unicode_prop_Changes_When_Casefolded1_table,
+ unicode_prop_Changes_When_NFKC_Casefolded1_table,
+ unicode_prop_ASCII_Hex_Digit_table,
+ unicode_prop_Bidi_Control_table,
+ unicode_prop_Dash_table,
+ unicode_prop_Deprecated_table,
+ unicode_prop_Diacritic_table,
+ unicode_prop_Extender_table,
+ unicode_prop_Hex_Digit_table,
+ unicode_prop_IDS_Binary_Operator_table,
+ unicode_prop_IDS_Trinary_Operator_table,
+ unicode_prop_Ideographic_table,
+ unicode_prop_Join_Control_table,
+ unicode_prop_Logical_Order_Exception_table,
+ unicode_prop_Noncharacter_Code_Point_table,
+ unicode_prop_Pattern_Syntax_table,
+ unicode_prop_Pattern_White_Space_table,
+ unicode_prop_Quotation_Mark_table,
+ unicode_prop_Radical_table,
+ unicode_prop_Regional_Indicator_table,
+ unicode_prop_Sentence_Terminal_table,
+ unicode_prop_Soft_Dotted_table,
+ unicode_prop_Terminal_Punctuation_table,
+ unicode_prop_Unified_Ideograph_table,
+ unicode_prop_Variation_Selector_table,
+ unicode_prop_White_Space_table,
+ unicode_prop_Bidi_Mirrored_table,
+ unicode_prop_Emoji_table,
+ unicode_prop_Emoji_Component_table,
+ unicode_prop_Emoji_Modifier_table,
+ unicode_prop_Emoji_Modifier_Base_table,
+ unicode_prop_Emoji_Presentation_table,
+ unicode_prop_Extended_Pictographic_table,
+ unicode_prop_Default_Ignorable_Code_Point_table,
+ unicode_prop_ID_Start_table,
+ unicode_prop_Case_Ignorable_table,
+};
+
+static const uint16_t unicode_prop_len_table[] = {
+ countof(unicode_prop_Hyphen_table),
+ countof(unicode_prop_Other_Math_table),
+ countof(unicode_prop_Other_Alphabetic_table),
+ countof(unicode_prop_Other_Lowercase_table),
+ countof(unicode_prop_Other_Uppercase_table),
+ countof(unicode_prop_Other_Grapheme_Extend_table),
+ countof(unicode_prop_Other_Default_Ignorable_Code_Point_table),
+ countof(unicode_prop_Other_ID_Start_table),
+ countof(unicode_prop_Other_ID_Continue_table),
+ countof(unicode_prop_Prepended_Concatenation_Mark_table),
+ countof(unicode_prop_ID_Continue1_table),
+ countof(unicode_prop_XID_Start1_table),
+ countof(unicode_prop_XID_Continue1_table),
+ countof(unicode_prop_Changes_When_Titlecased1_table),
+ countof(unicode_prop_Changes_When_Casefolded1_table),
+ countof(unicode_prop_Changes_When_NFKC_Casefolded1_table),
+ countof(unicode_prop_ASCII_Hex_Digit_table),
+ countof(unicode_prop_Bidi_Control_table),
+ countof(unicode_prop_Dash_table),
+ countof(unicode_prop_Deprecated_table),
+ countof(unicode_prop_Diacritic_table),
+ countof(unicode_prop_Extender_table),
+ countof(unicode_prop_Hex_Digit_table),
+ countof(unicode_prop_IDS_Binary_Operator_table),
+ countof(unicode_prop_IDS_Trinary_Operator_table),
+ countof(unicode_prop_Ideographic_table),
+ countof(unicode_prop_Join_Control_table),
+ countof(unicode_prop_Logical_Order_Exception_table),
+ countof(unicode_prop_Noncharacter_Code_Point_table),
+ countof(unicode_prop_Pattern_Syntax_table),
+ countof(unicode_prop_Pattern_White_Space_table),
+ countof(unicode_prop_Quotation_Mark_table),
+ countof(unicode_prop_Radical_table),
+ countof(unicode_prop_Regional_Indicator_table),
+ countof(unicode_prop_Sentence_Terminal_table),
+ countof(unicode_prop_Soft_Dotted_table),
+ countof(unicode_prop_Terminal_Punctuation_table),
+ countof(unicode_prop_Unified_Ideograph_table),
+ countof(unicode_prop_Variation_Selector_table),
+ countof(unicode_prop_White_Space_table),
+ countof(unicode_prop_Bidi_Mirrored_table),
+ countof(unicode_prop_Emoji_table),
+ countof(unicode_prop_Emoji_Component_table),
+ countof(unicode_prop_Emoji_Modifier_table),
+ countof(unicode_prop_Emoji_Modifier_Base_table),
+ countof(unicode_prop_Emoji_Presentation_table),
+ countof(unicode_prop_Extended_Pictographic_table),
+ countof(unicode_prop_Default_Ignorable_Code_Point_table),
+ countof(unicode_prop_ID_Start_table),
+ countof(unicode_prop_Case_Ignorable_table),
+};
+
+#endif /* CONFIG_ALL_UNICODE */
diff --git a/src/shared/quickjs/libunicode.c b/src/shared/quickjs/libunicode.c
new file mode 100644
index 000000000..112da72da
--- /dev/null
+++ b/src/shared/quickjs/libunicode.c
@@ -0,0 +1,1558 @@
+/*
+ * 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.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "libunicode.h"
+#include "libunicode-table.h"
+
+enum {
+ RUN_TYPE_U,
+ RUN_TYPE_L,
+ RUN_TYPE_UF,
+ RUN_TYPE_LF,
+ RUN_TYPE_UL,
+ RUN_TYPE_LSU,
+ RUN_TYPE_U2L_399_EXT2,
+ RUN_TYPE_UF_D20,
+ RUN_TYPE_UF_D1_EXT,
+ RUN_TYPE_U_EXT,
+ RUN_TYPE_LF_EXT,
+ RUN_TYPE_U_EXT2,
+ RUN_TYPE_L_EXT2,
+ RUN_TYPE_U_EXT3,
+};
+
+/* conv_type:
+ 0 = to upper
+ 1 = to lower
+ 2 = case folding (= to lower with modifications)
+*/
+int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
+{
+ if (c < 128) {
+ if (conv_type) {
+ if (c >= 'A' && c <= 'Z') {
+ c = c - 'A' + 'a';
+ }
+ } else {
+ if (c >= 'a' && c <= 'z') {
+ c = c - 'a' + 'A';
+ }
+ }
+ } else {
+ uint32_t v, code, data, type, len, a, is_lower;
+ int idx, idx_min, idx_max;
+
+ is_lower = (conv_type != 0);
+ idx_min = 0;
+ idx_max = countof(case_conv_table1) - 1;
+ while (idx_min <= idx_max) {
+ idx = (unsigned)(idx_max + idx_min) / 2;
+ v = case_conv_table1[idx];
+ code = v >> (32 - 17);
+ len = (v >> (32 - 17 - 7)) & 0x7f;
+ if (c < code) {
+ idx_max = idx - 1;
+ } else if (c >= code + len) {
+ idx_min = idx + 1;
+ } else {
+ type = (v >> (32 - 17 - 7 - 4)) & 0xf;
+ data = ((v & 0xf) << 8) | case_conv_table2[idx];
+ switch(type) {
+ case RUN_TYPE_U:
+ case RUN_TYPE_L:
+ case RUN_TYPE_UF:
+ case RUN_TYPE_LF:
+ if (conv_type == (type & 1) ||
+ (type >= RUN_TYPE_UF && conv_type == 2)) {
+ c = c - code + (case_conv_table1[data] >> (32 - 17));
+ }
+ break;
+ case RUN_TYPE_UL:
+ a = c - code;
+ if ((a & 1) != (1 - is_lower))
+ break;
+ c = (a ^ 1) + code;
+ break;
+ case RUN_TYPE_LSU:
+ a = c - code;
+ if (a == 1) {
+ c += 2 * is_lower - 1;
+ } else if (a == (1 - is_lower) * 2) {
+ c += (2 * is_lower - 1) * 2;
+ }
+ break;
+ case RUN_TYPE_U2L_399_EXT2:
+ if (!is_lower) {
+ res[0] = c - code + case_conv_ext[data >> 6];
+ res[1] = 0x399;
+ return 2;
+ } else {
+ c = c - code + case_conv_ext[data & 0x3f];
+ }
+ break;
+ case RUN_TYPE_UF_D20:
+ if (conv_type == 1)
+ break;
+ c = data + (conv_type == 2) * 0x20;
+ break;
+ case RUN_TYPE_UF_D1_EXT:
+ if (conv_type == 1)
+ break;
+ c = case_conv_ext[data] + (conv_type == 2);
+ break;
+ case RUN_TYPE_U_EXT:
+ case RUN_TYPE_LF_EXT:
+ if (is_lower != (type - RUN_TYPE_U_EXT))
+ break;
+ c = case_conv_ext[data];
+ break;
+ case RUN_TYPE_U_EXT2:
+ case RUN_TYPE_L_EXT2:
+ if (conv_type != (type - RUN_TYPE_U_EXT2))
+ break;
+ res[0] = c - code + case_conv_ext[data >> 6];
+ res[1] = case_conv_ext[data & 0x3f];
+ return 2;
+ default:
+ case RUN_TYPE_U_EXT3:
+ if (conv_type != 0)
+ break;
+ res[0] = case_conv_ext[data >> 8];
+ res[1] = case_conv_ext[(data >> 4) & 0xf];
+ res[2] = case_conv_ext[data & 0xf];
+ return 3;
+ }
+ break;
+ }
+ }
+ }
+ res[0] = c;
+ return 1;
+}
+
+static uint32_t get_le24(const uint8_t *ptr)
+{
+#if defined(__x86__) || defined(__x86_64__)
+ return *(uint16_t *)ptr | (ptr[2] << 16);
+#else
+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);
+#endif
+}
+
+#define UNICODE_INDEX_BLOCK_LEN 32
+
+/* return -1 if not in table, otherwise the offset in the block */
+static int get_index_pos(uint32_t *pcode, uint32_t c,
+ const uint8_t *index_table, int index_table_len)
+{
+ uint32_t code, v;
+ int idx_min, idx_max, idx;
+
+ idx_min = 0;
+ v = get_le24(index_table);
+ code = v & ((1 << 21) - 1);
+ if (c < code) {
+ *pcode = 0;
+ return 0;
+ }
+ idx_max = index_table_len - 1;
+ code = get_le24(index_table + idx_max * 3);
+ if (c >= code)
+ return -1;
+ /* invariant: tab[idx_min] <= c < tab2[idx_max] */
+ while ((idx_max - idx_min) > 1) {
+ idx = (idx_max + idx_min) / 2;
+ v = get_le24(index_table + idx * 3);
+ code = v & ((1 << 21) - 1);
+ if (c < code) {
+ idx_max = idx;
+ } else {
+ idx_min = idx;
+ }
+ }
+ v = get_le24(index_table + idx_min * 3);
+ *pcode = v & ((1 << 21) - 1);
+ return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21);
+}
+
+static BOOL lre_is_in_table(uint32_t c, const uint8_t *table,
+ const uint8_t *index_table, int index_table_len)
+{
+ uint32_t code, b, bit;
+ int pos;
+ const uint8_t *p;
+
+ pos = get_index_pos(&code, c, index_table, index_table_len);
+ if (pos < 0)
+ return FALSE; /* outside the table */
+ p = table + pos;
+ bit = 0;
+ for(;;) {
+ b = *p++;
+ if (b < 64) {
+ code += (b >> 3) + 1;
+ if (c < code)
+ return bit;
+ bit ^= 1;
+ code += (b & 7) + 1;
+ } else if (b >= 0x80) {
+ code += b - 0x80 + 1;
+ } else if (b < 0x60) {
+ code += (((b - 0x40) << 8) | p[0]) + 1;
+ p++;
+ } else {
+ code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1;
+ p += 2;
+ }
+ if (c < code)
+ return bit;
+ bit ^= 1;
+ }
+}
+
+BOOL lre_is_cased(uint32_t c)
+{
+ uint32_t v, code, len;
+ int idx, idx_min, idx_max;
+
+ idx_min = 0;
+ idx_max = countof(case_conv_table1) - 1;
+ while (idx_min <= idx_max) {
+ idx = (unsigned)(idx_max + idx_min) / 2;
+ v = case_conv_table1[idx];
+ code = v >> (32 - 17);
+ len = (v >> (32 - 17 - 7)) & 0x7f;
+ if (c < code) {
+ idx_max = idx - 1;
+ } else if (c >= code + len) {
+ idx_min = idx + 1;
+ } else {
+ return TRUE;
+ }
+ }
+ return lre_is_in_table(c, unicode_prop_Cased1_table,
+ unicode_prop_Cased1_index,
+ sizeof(unicode_prop_Cased1_index) / 3);
+}
+
+BOOL lre_is_case_ignorable(uint32_t c)
+{
+ return lre_is_in_table(c, unicode_prop_Case_Ignorable_table,
+ unicode_prop_Case_Ignorable_index,
+ sizeof(unicode_prop_Case_Ignorable_index) / 3);
+}
+
+/* character range */
+
+static maybe_unused void cr_dump(CharRange *cr)
+{
+ int i;
+ for(i = 0; i < cr->len; i++)
+ printf("%d: 0x%04x\n", i, cr->points[i]);
+}
+
+static void *cr_default_realloc(void *opaque, void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func)
+{
+ cr->len = cr->size = 0;
+ cr->points = NULL;
+ cr->mem_opaque = mem_opaque;
+ cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc;
+}
+
+void cr_free(CharRange *cr)
+{
+ cr->realloc_func(cr->mem_opaque, cr->points, 0);
+}
+
+int cr_realloc(CharRange *cr, int size)
+{
+ int new_size;
+ uint32_t *new_buf;
+
+ if (size > cr->size) {
+ new_size = max_int(size, cr->size * 3 / 2);
+ new_buf = cr->realloc_func(cr->mem_opaque, cr->points,
+ new_size * sizeof(cr->points[0]));
+ if (!new_buf)
+ return -1;
+ cr->points = new_buf;
+ cr->size = new_size;
+ }
+ return 0;
+}
+
+int cr_copy(CharRange *cr, const CharRange *cr1)
+{
+ if (cr_realloc(cr, cr1->len))
+ return -1;
+ memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len);
+ cr->len = cr1->len;
+ return 0;
+}
+
+/* merge consecutive intervals and remove empty intervals */
+static void cr_compress(CharRange *cr)
+{
+ int i, j, k, len;
+ uint32_t *pt;
+
+ pt = cr->points;
+ len = cr->len;
+ i = 0;
+ j = 0;
+ k = 0;
+ while ((i + 1) < len) {
+ if (pt[i] == pt[i + 1]) {
+ /* empty interval */
+ i += 2;
+ } else {
+ j = i;
+ while ((j + 3) < len && pt[j + 1] == pt[j + 2])
+ j += 2;
+ /* just copy */
+ pt[k] = pt[i];
+ pt[k + 1] = pt[j + 1];
+ k += 2;
+ i = j + 2;
+ }
+ }
+ cr->len = k;
+}
+
+/* union or intersection */
+int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
+ const uint32_t *b_pt, int b_len, int op)
+{
+ int a_idx, b_idx, is_in;
+ uint32_t v;
+
+ a_idx = 0;
+ b_idx = 0;
+ for(;;) {
+ /* get one more point from a or b in increasing order */
+ if (a_idx < a_len && b_idx < b_len) {
+ if (a_pt[a_idx] < b_pt[b_idx]) {
+ goto a_add;
+ } else if (a_pt[a_idx] == b_pt[b_idx]) {
+ v = a_pt[a_idx];
+ a_idx++;
+ b_idx++;
+ } else {
+ goto b_add;
+ }
+ } else if (a_idx < a_len) {
+ a_add:
+ v = a_pt[a_idx++];
+ } else if (b_idx < b_len) {
+ b_add:
+ v = b_pt[b_idx++];
+ } else {
+ break;
+ }
+ /* add the point if the in/out status changes */
+ switch(op) {
+ case CR_OP_UNION:
+ is_in = (a_idx & 1) | (b_idx & 1);
+ break;
+ case CR_OP_INTER:
+ is_in = (a_idx & 1) & (b_idx & 1);
+ break;
+ case CR_OP_XOR:
+ is_in = (a_idx & 1) ^ (b_idx & 1);
+ break;
+ default:
+ abort();
+ }
+ if (is_in != (cr->len & 1)) {
+ if (cr_add_point(cr, v))
+ return -1;
+ }
+ }
+ cr_compress(cr);
+ return 0;
+}
+
+int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len)
+{
+ CharRange a = *cr;
+ int ret;
+ cr->len = 0;
+ cr->size = 0;
+ cr->points = NULL;
+ ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION);
+ cr_free(&a);
+ return ret;
+}
+
+int cr_invert(CharRange *cr)
+{
+ int len;
+ len = cr->len;
+ if (cr_realloc(cr, len + 2))
+ return -1;
+ memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0]));
+ cr->points[0] = 0;
+ cr->points[len + 1] = UINT32_MAX;
+ cr->len = len + 2;
+ cr_compress(cr);
+ return 0;
+}
+
+#ifdef CONFIG_ALL_UNICODE
+
+BOOL lre_is_id_start(uint32_t c)
+{
+ return lre_is_in_table(c, unicode_prop_ID_Start_table,
+ unicode_prop_ID_Start_index,
+ sizeof(unicode_prop_ID_Start_index) / 3);
+}
+
+BOOL lre_is_id_continue(uint32_t c)
+{
+ return lre_is_id_start(c) ||
+ lre_is_in_table(c, unicode_prop_ID_Continue1_table,
+ unicode_prop_ID_Continue1_index,
+ sizeof(unicode_prop_ID_Continue1_index) / 3);
+}
+
+#define UNICODE_DECOMP_LEN_MAX 18
+
+typedef enum {
+ DECOMP_TYPE_C1, /* 16 bit char */
+ DECOMP_TYPE_L1, /* 16 bit char table */
+ DECOMP_TYPE_L2,
+ DECOMP_TYPE_L3,
+ DECOMP_TYPE_L4,
+ DECOMP_TYPE_L5, /* XXX: not used */
+ DECOMP_TYPE_L6, /* XXX: could remove */
+ DECOMP_TYPE_L7, /* XXX: could remove */
+ DECOMP_TYPE_LL1, /* 18 bit char table */
+ DECOMP_TYPE_LL2,
+ DECOMP_TYPE_S1, /* 8 bit char table */
+ DECOMP_TYPE_S2,
+ DECOMP_TYPE_S3,
+ DECOMP_TYPE_S4,
+ DECOMP_TYPE_S5,
+ DECOMP_TYPE_I1, /* increment 16 bit char value */
+ DECOMP_TYPE_I2_0,
+ DECOMP_TYPE_I2_1,
+ DECOMP_TYPE_I3_1,
+ DECOMP_TYPE_I3_2,
+ DECOMP_TYPE_I4_1,
+ DECOMP_TYPE_I4_2,
+ DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */
+ DECOMP_TYPE_B2,
+ DECOMP_TYPE_B3,
+ DECOMP_TYPE_B4,
+ DECOMP_TYPE_B5,
+ DECOMP_TYPE_B6,
+ DECOMP_TYPE_B7,
+ DECOMP_TYPE_B8,
+ DECOMP_TYPE_B18,
+ DECOMP_TYPE_LS2,
+ DECOMP_TYPE_PAT3,
+ DECOMP_TYPE_S2_UL,
+ DECOMP_TYPE_LS2_UL,
+} DecompTypeEnum;
+
+static uint32_t unicode_get_short_code(uint32_t c)
+{
+ static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 };
+
+ if (c < 0x80)
+ return c;
+ else if (c < 0x80 + 0x50)
+ return c - 0x80 + 0x300;
+ else
+ return unicode_short_table[c - 0x80 - 0x50];
+}
+
+static uint32_t unicode_get_lower_simple(uint32_t c)
+{
+ if (c < 0x100 || (c >= 0x410 && c <= 0x42f))
+ c += 0x20;
+ else
+ c++;
+ return c;
+}
+
+static uint16_t unicode_get16(const uint8_t *p)
+{
+ return p[0] | (p[1] << 8);
+}
+
+static int unicode_decomp_entry(uint32_t *res, uint32_t c,
+ int idx, uint32_t code, uint32_t len,
+ uint32_t type)
+{
+ uint32_t c1;
+ int l, i, p;
+ const uint8_t *d;
+
+ if (type == DECOMP_TYPE_C1) {
+ res[0] = unicode_decomp_table2[idx];
+ return 1;
+ } else {
+ d = unicode_decomp_data + unicode_decomp_table2[idx];
+ switch(type) {
+ case DECOMP_TYPE_L1:
+ case DECOMP_TYPE_L2:
+ case DECOMP_TYPE_L3:
+ case DECOMP_TYPE_L4:
+ case DECOMP_TYPE_L5:
+ case DECOMP_TYPE_L6:
+ case DECOMP_TYPE_L7:
+ l = type - DECOMP_TYPE_L1 + 1;
+ d += (c - code) * l * 2;
+ for(i = 0; i < l; i++) {
+ if ((res[i] = unicode_get16(d + 2 * i)) == 0)
+ return 0;
+ }
+ return l;
+ case DECOMP_TYPE_LL1:
+ case DECOMP_TYPE_LL2:
+ {
+ uint32_t k, p;
+ l = type - DECOMP_TYPE_LL1 + 1;
+ k = (c - code) * l;
+ p = len * l * 2;
+ for(i = 0; i < l; i++) {
+ c1 = unicode_get16(d + 2 * k) |
+ (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16);
+ if (!c1)
+ return 0;
+ res[i] = c1;
+ k++;
+ }
+ }
+ return l;
+ case DECOMP_TYPE_S1:
+ case DECOMP_TYPE_S2:
+ case DECOMP_TYPE_S3:
+ case DECOMP_TYPE_S4:
+ case DECOMP_TYPE_S5:
+ l = type - DECOMP_TYPE_S1 + 1;
+ d += (c - code) * l;
+ for(i = 0; i < l; i++) {
+ if ((res[i] = unicode_get_short_code(d[i])) == 0)
+ return 0;
+ }
+ return l;
+ case DECOMP_TYPE_I1:
+ l = 1;
+ p = 0;
+ goto decomp_type_i;
+ case DECOMP_TYPE_I2_0:
+ case DECOMP_TYPE_I2_1:
+ case DECOMP_TYPE_I3_1:
+ case DECOMP_TYPE_I3_2:
+ case DECOMP_TYPE_I4_1:
+ case DECOMP_TYPE_I4_2:
+ l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1);
+ p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2);
+ decomp_type_i:
+ for(i = 0; i < l; i++) {
+ c1 = unicode_get16(d + 2 * i);
+ if (i == p)
+ c1 += c - code;
+ res[i] = c1;
+ }
+ return l;
+ case DECOMP_TYPE_B18:
+ l = 18;
+ goto decomp_type_b;
+ case DECOMP_TYPE_B1:
+ case DECOMP_TYPE_B2:
+ case DECOMP_TYPE_B3:
+ case DECOMP_TYPE_B4:
+ case DECOMP_TYPE_B5:
+ case DECOMP_TYPE_B6:
+ case DECOMP_TYPE_B7:
+ case DECOMP_TYPE_B8:
+ l = type - DECOMP_TYPE_B1 + 1;
+ decomp_type_b:
+ {
+ uint32_t c_min;
+ c_min = unicode_get16(d);
+ d += 2 + (c - code) * l;
+ for(i = 0; i < l; i++) {
+ c1 = d[i];
+ if (c1 == 0xff)
+ c1 = 0x20;
+ else
+ c1 += c_min;
+ res[i] = c1;
+ }
+ }
+ return l;
+ case DECOMP_TYPE_LS2:
+ d += (c - code) * 3;
+ if (!(res[0] = unicode_get16(d)))
+ return 0;
+ res[1] = unicode_get_short_code(d[2]);
+ return 2;
+ case DECOMP_TYPE_PAT3:
+ res[0] = unicode_get16(d);
+ res[2] = unicode_get16(d + 2);
+ d += 4 + (c - code) * 2;
+ res[1] = unicode_get16(d);
+ return 3;
+ case DECOMP_TYPE_S2_UL:
+ case DECOMP_TYPE_LS2_UL:
+ c1 = c - code;
+ if (type == DECOMP_TYPE_S2_UL) {
+ d += c1 & ~1;
+ c = unicode_get_short_code(*d);
+ d++;
+ } else {
+ d += (c1 >> 1) * 3;
+ c = unicode_get16(d);
+ d += 2;
+ }
+ if (c1 & 1)
+ c = unicode_get_lower_simple(c);
+ res[0] = c;
+ res[1] = unicode_get_short_code(*d);
+ return 2;
+ }
+ }
+ return 0;
+}
+
+
+/* return the length of the decomposition (length <=
+ UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */
+static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1)
+{
+ uint32_t v, type, is_compat, code, len;
+ int idx_min, idx_max, idx;
+
+ idx_min = 0;
+ idx_max = countof(unicode_decomp_table1) - 1;
+ while (idx_min <= idx_max) {
+ idx = (idx_max + idx_min) / 2;
+ v = unicode_decomp_table1[idx];
+ code = v >> (32 - 18);
+ len = (v >> (32 - 18 - 7)) & 0x7f;
+ // printf("idx=%d code=%05x len=%d\n", idx, code, len);
+ if (c < code) {
+ idx_max = idx - 1;
+ } else if (c >= code + len) {
+ idx_min = idx + 1;
+ } else {
+ is_compat = v & 1;
+ if (is_compat1 < is_compat)
+ break;
+ type = (v >> (32 - 18 - 7 - 6)) & 0x3f;
+ return unicode_decomp_entry(res, c, idx, code, len, type);
+ }
+ }
+ return 0;
+}
+
+/* return 0 if no pair found */
+static int unicode_compose_pair(uint32_t c0, uint32_t c1)
+{
+ uint32_t code, len, type, v, idx1, d_idx, d_offset, ch;
+ int idx_min, idx_max, idx, d;
+ uint32_t pair[2];
+
+ idx_min = 0;
+ idx_max = countof(unicode_comp_table) - 1;
+ while (idx_min <= idx_max) {
+ idx = (idx_max + idx_min) / 2;
+ idx1 = unicode_comp_table[idx];
+
+ /* idx1 represent an entry of the decomposition table */
+ d_idx = idx1 >> 6;
+ d_offset = idx1 & 0x3f;
+ v = unicode_decomp_table1[d_idx];
+ code = v >> (32 - 18);
+ len = (v >> (32 - 18 - 7)) & 0x7f;
+ type = (v >> (32 - 18 - 7 - 6)) & 0x3f;
+ ch = code + d_offset;
+ unicode_decomp_entry(pair, ch, d_idx, code, len, type);
+ d = c0 - pair[0];
+ if (d == 0)
+ d = c1 - pair[1];
+ if (d < 0) {
+ idx_max = idx - 1;
+ } else if (d > 0) {
+ idx_min = idx + 1;
+ } else {
+ return ch;
+ }
+ }
+ return 0;
+}
+
+/* return the combining class of character c (between 0 and 255) */
+static int unicode_get_cc(uint32_t c)
+{
+ uint32_t code, n, type, cc, c1, b;
+ int pos;
+ const uint8_t *p;
+
+ pos = get_index_pos(&code, c,
+ unicode_cc_index, sizeof(unicode_cc_index) / 3);
+ if (pos < 0)
+ return 0;
+ p = unicode_cc_table + pos;
+ for(;;) {
+ b = *p++;
+ type = b >> 6;
+ n = b & 0x3f;
+ if (n < 48) {
+ } else if (n < 56) {
+ n = (n - 48) << 8;
+ n |= *p++;
+ n += 48;
+ } else {
+ n = (n - 56) << 8;
+ n |= *p++ << 8;
+ n |= *p++;
+ n += 48 + (1 << 11);
+ }
+ if (type <= 1)
+ p++;
+ c1 = code + n + 1;
+ if (c < c1) {
+ switch(type) {
+ case 0:
+ cc = p[-1];
+ break;
+ case 1:
+ cc = p[-1] + c - code;
+ break;
+ case 2:
+ cc = 0;
+ break;
+ default:
+ case 3:
+ cc = 230;
+ break;
+ }
+ return cc;
+ }
+ code = c1;
+ }
+}
+
+static void sort_cc(int *buf, int len)
+{
+ int i, j, k, cc, cc1, start, ch1;
+
+ for(i = 0; i < len; i++) {
+ cc = unicode_get_cc(buf[i]);
+ if (cc != 0) {
+ start = i;
+ j = i + 1;
+ while (j < len) {
+ ch1 = buf[j];
+ cc1 = unicode_get_cc(ch1);
+ if (cc1 == 0)
+ break;
+ k = j - 1;
+ while (k >= start) {
+ if (unicode_get_cc(buf[k]) <= cc1)
+ break;
+ buf[k + 1] = buf[k];
+ k--;
+ }
+ buf[k + 1] = ch1;
+ j++;
+ }
+#if 0
+ printf("cc:");
+ for(k = start; k < j; k++) {
+ printf(" %3d", unicode_get_cc(buf[k]));
+ }
+ printf("\n");
+#endif
+ i = j;
+ }
+ }
+}
+
+static void to_nfd_rec(DynBuf *dbuf,
+ const int *src, int src_len, int is_compat)
+{
+ uint32_t c, v;
+ int i, l;
+ uint32_t res[UNICODE_DECOMP_LEN_MAX];
+
+ for(i = 0; i < src_len; i++) {
+ c = src[i];
+ if (c >= 0xac00 && c < 0xd7a4) {
+ /* Hangul decomposition */
+ c -= 0xac00;
+ dbuf_put_u32(dbuf, 0x1100 + c / 588);
+ dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28);
+ v = c % 28;
+ if (v != 0)
+ dbuf_put_u32(dbuf, 0x11a7 + v);
+ } else {
+ l = unicode_decomp_char(res, c, is_compat);
+ if (l) {
+ to_nfd_rec(dbuf, (int *)res, l, is_compat);
+ } else {
+ dbuf_put_u32(dbuf, c);
+ }
+ }
+ }
+}
+
+/* return 0 if not found */
+static int compose_pair(uint32_t c0, uint32_t c1)
+{
+ /* Hangul composition */
+ if (c0 >= 0x1100 && c0 < 0x1100 + 19 &&
+ c1 >= 0x1161 && c1 < 0x1161 + 21) {
+ return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28;
+ } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 &&
+ (c0 - 0xac00) % 28 == 0 &&
+ c1 >= 0x11a7 && c1 < 0x11a7 + 28) {
+ return c0 + c1 - 0x11a7;
+ } else {
+ return unicode_compose_pair(c0, c1);
+ }
+}
+
+int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
+ UnicodeNormalizationEnum n_type,
+ void *opaque, DynBufReallocFunc *realloc_func)
+{
+ int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len;
+ BOOL is_compat;
+ DynBuf dbuf_s, *dbuf = &dbuf_s;
+
+ is_compat = n_type >> 1;
+
+ dbuf_init2(dbuf, opaque, realloc_func);
+ if (dbuf_realloc(dbuf, sizeof(int) * src_len))
+ goto fail;
+
+ /* common case: latin1 is unaffected by NFC */
+ if (n_type == UNICODE_NFC) {
+ for(i = 0; i < src_len; i++) {
+ if (src[i] >= 0x100)
+ goto not_latin1;
+ }
+ buf = (int *)dbuf->buf;
+ memcpy(buf, src, src_len * sizeof(int));
+ *pdst = (uint32_t *)buf;
+ return src_len;
+ not_latin1: ;
+ }
+
+ to_nfd_rec(dbuf, (const int *)src, src_len, is_compat);
+ if (dbuf_error(dbuf)) {
+ fail:
+ *pdst = NULL;
+ return -1;
+ }
+ buf = (int *)dbuf->buf;
+ buf_len = dbuf->size / sizeof(int);
+
+ sort_cc(buf, buf_len);
+
+ if (buf_len <= 1 || (n_type & 1) != 0) {
+ /* NFD / NFKD */
+ *pdst = (uint32_t *)buf;
+ return buf_len;
+ }
+
+ i = 1;
+ out_len = 1;
+ while (i < buf_len) {
+ /* find the starter character and test if it is blocked from
+ the character at 'i' */
+ last_cc = unicode_get_cc(buf[i]);
+ starter_pos = out_len - 1;
+ while (starter_pos >= 0) {
+ cc = unicode_get_cc(buf[starter_pos]);
+ if (cc == 0)
+ break;
+ if (cc >= last_cc)
+ goto next;
+ last_cc = 256;
+ starter_pos--;
+ }
+ if (starter_pos >= 0 &&
+ (p = compose_pair(buf[starter_pos], buf[i])) != 0) {
+ buf[starter_pos] = p;
+ i++;
+ } else {
+ next:
+ buf[out_len++] = buf[i++];
+ }
+ }
+ *pdst = (uint32_t *)buf;
+ return out_len;
+}
+
+/* char ranges for various unicode properties */
+
+static int unicode_find_name(const char *name_table, const char *name)
+{
+ const char *p, *r;
+ int pos;
+ size_t name_len, len;
+
+ p = name_table;
+ pos = 0;
+ name_len = strlen(name);
+ while (*p) {
+ for(;;) {
+ r = strchr(p, ',');
+ if (!r)
+ len = strlen(p);
+ else
+ len = r - p;
+ if (len == name_len && !memcmp(p, name, name_len))
+ return pos;
+ p += len + 1;
+ if (!r)
+ break;
+ }
+ pos++;
+ }
+ return -1;
+}
+
+/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2
+ if not found */
+int unicode_script(CharRange *cr,
+ const char *script_name, BOOL is_ext)
+{
+ int script_idx;
+ const uint8_t *p, *p_end;
+ uint32_t c, c1, b, n, v, v_len, i, type;
+ CharRange cr1_s, *cr1;
+ CharRange cr2_s, *cr2 = &cr2_s;
+ BOOL is_common;
+
+ script_idx = unicode_find_name(unicode_script_name_table, script_name);
+ if (script_idx < 0)
+ return -2;
+ /* Note: we remove the "Unknown" Script */
+ script_idx += UNICODE_SCRIPT_Unknown + 1;
+
+ is_common = (script_idx == UNICODE_SCRIPT_Common ||
+ script_idx == UNICODE_SCRIPT_Inherited);
+ if (is_ext) {
+ cr1 = &cr1_s;
+ cr_init(cr1, cr->mem_opaque, cr->realloc_func);
+ cr_init(cr2, cr->mem_opaque, cr->realloc_func);
+ } else {
+ cr1 = cr;
+ }
+
+ p = unicode_script_table;
+ p_end = unicode_script_table + countof(unicode_script_table);
+ c = 0;
+ while (p < p_end) {
+ b = *p++;
+ type = b >> 7;
+ n = b & 0x7f;
+ if (n < 96) {
+ } else if (n < 112) {
+ n = (n - 96) << 8;
+ n |= *p++;
+ n += 96;
+ } else {
+ n = (n - 112) << 16;
+ n |= *p++ << 8;
+ n |= *p++;
+ n += 96 + (1 << 12);
+ }
+ if (type == 0)
+ v = 0;
+ else
+ v = *p++;
+ c1 = c + n + 1;
+ if (v == script_idx) {
+ if (cr_add_interval(cr1, c, c1))
+ goto fail;
+ }
+ c = c1;
+ }
+
+ if (is_ext) {
+ /* add the script extensions */
+ p = unicode_script_ext_table;
+ p_end = unicode_script_ext_table + countof(unicode_script_ext_table);
+ c = 0;
+ while (p < p_end) {
+ b = *p++;
+ if (b < 128) {
+ n = b;
+ } else if (b < 128 + 64) {
+ n = (b - 128) << 8;
+ n |= *p++;
+ n += 128;
+ } else {
+ n = (b - 128 - 64) << 16;
+ n |= *p++ << 8;
+ n |= *p++;
+ n += 128 + (1 << 14);
+ }
+ c1 = c + n + 1;
+ v_len = *p++;
+ if (is_common) {
+ if (v_len != 0) {
+ if (cr_add_interval(cr2, c, c1))
+ goto fail;
+ }
+ } else {
+ for(i = 0; i < v_len; i++) {
+ if (p[i] == script_idx) {
+ if (cr_add_interval(cr2, c, c1))
+ goto fail;
+ break;
+ }
+ }
+ }
+ p += v_len;
+ c = c1;
+ }
+ if (is_common) {
+ /* remove all the characters with script extensions */
+ if (cr_invert(cr2))
+ goto fail;
+ if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len,
+ CR_OP_INTER))
+ goto fail;
+ } else {
+ if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len,
+ CR_OP_UNION))
+ goto fail;
+ }
+ cr_free(cr1);
+ cr_free(cr2);
+ }
+ return 0;
+ fail:
+ if (is_ext) {
+ cr_free(cr1);
+ cr_free(cr2);
+ }
+ goto fail;
+}
+
+#define M(id) (1U << UNICODE_GC_ ## id)
+
+static int unicode_general_category1(CharRange *cr, uint32_t gc_mask)
+{
+ const uint8_t *p, *p_end;
+ uint32_t c, c0, b, n, v;
+
+ p = unicode_gc_table;
+ p_end = unicode_gc_table + countof(unicode_gc_table);
+ c = 0;
+ while (p < p_end) {
+ b = *p++;
+ n = b >> 5;
+ v = b & 0x1f;
+ if (n == 7) {
+ n = *p++;
+ if (n < 128) {
+ n += 7;
+ } else if (n < 128 + 64) {
+ n = (n - 128) << 8;
+ n |= *p++;
+ n += 7 + 128;
+ } else {
+ n = (n - 128 - 64) << 16;
+ n |= *p++ << 8;
+ n |= *p++;
+ n += 7 + 128 + (1 << 14);
+ }
+ }
+ c0 = c;
+ c += n + 1;
+ if (v == 31) {
+ /* run of Lu / Ll */
+ b = gc_mask & (M(Lu) | M(Ll));
+ if (b != 0) {
+ if (b == (M(Lu) | M(Ll))) {
+ goto add_range;
+ } else {
+ c0 += ((gc_mask & M(Ll)) != 0);
+ for(; c0 < c; c0 += 2) {
+ if (cr_add_interval(cr, c0, c0 + 1))
+ return -1;
+ }
+ }
+ }
+ } else if ((gc_mask >> v) & 1) {
+ add_range:
+ if (cr_add_interval(cr, c0, c))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int unicode_prop1(CharRange *cr, int prop_idx)
+{
+ const uint8_t *p, *p_end;
+ uint32_t c, c0, b, bit;
+
+ p = unicode_prop_table[prop_idx];
+ p_end = p + unicode_prop_len_table[prop_idx];
+ c = 0;
+ bit = 0;
+ while (p < p_end) {
+ c0 = c;
+ b = *p++;
+ if (b < 64) {
+ c += (b >> 3) + 1;
+ if (bit) {
+ if (cr_add_interval(cr, c0, c))
+ return -1;
+ }
+ bit ^= 1;
+ c0 = c;
+ c += (b & 7) + 1;
+ } else if (b >= 0x80) {
+ c += b - 0x80 + 1;
+ } else if (b < 0x60) {
+ c += (((b - 0x40) << 8) | p[0]) + 1;
+ p++;
+ } else {
+ c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1;
+ p += 2;
+ }
+ if (bit) {
+ if (cr_add_interval(cr, c0, c))
+ return -1;
+ }
+ bit ^= 1;
+ }
+ return 0;
+}
+
+#define CASE_U (1 << 0)
+#define CASE_L (1 << 1)
+#define CASE_F (1 << 2)
+
+/* use the case conversion table to generate range of characters.
+ CASE_U: set char if modified by uppercasing,
+ CASE_L: set char if modified by lowercasing,
+ CASE_F: set char if modified by case folding,
+ */
+static int unicode_case1(CharRange *cr, int case_mask)
+{
+#define MR(x) (1 << RUN_TYPE_ ## x)
+ const uint32_t tab_run_mask[3] = {
+ MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) |
+ MR(UF_D1_EXT) | MR(U_EXT) | MR(U_EXT2) | MR(U_EXT3),
+
+ MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2),
+
+ MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT),
+ };
+#undef MR
+ uint32_t mask, v, code, type, len, i, idx;
+
+ if (case_mask == 0)
+ return 0;
+ mask = 0;
+ for(i = 0; i < 3; i++) {
+ if ((case_mask >> i) & 1)
+ mask |= tab_run_mask[i];
+ }
+ for(idx = 0; idx < countof(case_conv_table1); idx++) {
+ v = case_conv_table1[idx];
+ type = (v >> (32 - 17 - 7 - 4)) & 0xf;
+ code = v >> (32 - 17);
+ len = (v >> (32 - 17 - 7)) & 0x7f;
+ if ((mask >> type) & 1) {
+ // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1);
+ switch(type) {
+ case RUN_TYPE_UL:
+ if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F)))
+ goto def_case;
+ code += ((case_mask & CASE_U) != 0);
+ for(i = 0; i < len; i += 2) {
+ if (cr_add_interval(cr, code + i, code + i + 1))
+ return -1;
+ }
+ break;
+ case RUN_TYPE_LSU:
+ if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F)))
+ goto def_case;
+ if (!(case_mask & CASE_U)) {
+ if (cr_add_interval(cr, code, code + 1))
+ return -1;
+ }
+ if (cr_add_interval(cr, code + 1, code + 2))
+ return -1;
+ if (case_mask & CASE_U) {
+ if (cr_add_interval(cr, code + 2, code + 3))
+ return -1;
+ }
+ break;
+ default:
+ def_case:
+ if (cr_add_interval(cr, code, code + len))
+ return -1;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+typedef enum {
+ POP_GC,
+ POP_PROP,
+ POP_CASE,
+ POP_UNION,
+ POP_INTER,
+ POP_XOR,
+ POP_INVERT,
+ POP_END,
+} PropOPEnum;
+
+#define POP_STACK_LEN_MAX 4
+
+static int unicode_prop_ops(CharRange *cr, ...)
+{
+ va_list ap;
+ CharRange stack[POP_STACK_LEN_MAX];
+ int stack_len, op, ret, i;
+ uint32_t a;
+
+ va_start(ap, cr);
+ stack_len = 0;
+ for(;;) {
+ op = va_arg(ap, int);
+ switch(op) {
+ case POP_GC:
+ assert(stack_len < POP_STACK_LEN_MAX);
+ a = va_arg(ap, int);
+ cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);
+ if (unicode_general_category1(&stack[stack_len - 1], a))
+ goto fail;
+ break;
+ case POP_PROP:
+ assert(stack_len < POP_STACK_LEN_MAX);
+ a = va_arg(ap, int);
+ cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);
+ if (unicode_prop1(&stack[stack_len - 1], a))
+ goto fail;
+ break;
+ case POP_CASE:
+ assert(stack_len < POP_STACK_LEN_MAX);
+ a = va_arg(ap, int);
+ cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);
+ if (unicode_case1(&stack[stack_len - 1], a))
+ goto fail;
+ break;
+ case POP_UNION:
+ case POP_INTER:
+ case POP_XOR:
+ {
+ CharRange *cr1, *cr2, *cr3;
+ assert(stack_len >= 2);
+ assert(stack_len < POP_STACK_LEN_MAX);
+ cr1 = &stack[stack_len - 2];
+ cr2 = &stack[stack_len - 1];
+ cr3 = &stack[stack_len++];
+ cr_init(cr3, cr->mem_opaque, cr->realloc_func);
+ if (cr_op(cr3, cr1->points, cr1->len,
+ cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION))
+ goto fail;
+ cr_free(cr1);
+ cr_free(cr2);
+ *cr1 = *cr3;
+ stack_len -= 2;
+ }
+ break;
+ case POP_INVERT:
+ assert(stack_len >= 1);
+ if (cr_invert(&stack[stack_len - 1]))
+ goto fail;
+ break;
+ case POP_END:
+ goto done;
+ default:
+ abort();
+ }
+ }
+ done:
+ va_end(ap);
+ assert(stack_len == 1);
+ ret = cr_copy(cr, &stack[0]);
+ cr_free(&stack[0]);
+ return ret;
+ fail:
+ va_end(ap);
+ for(i = 0; i < stack_len; i++)
+ cr_free(&stack[i]);
+ return -1;
+}
+
+static const uint32_t unicode_gc_mask_table[] = {
+ M(Lu) | M(Ll) | M(Lt), /* LC */
+ M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */
+ M(Mn) | M(Mc) | M(Me), /* M */
+ M(Nd) | M(Nl) | M(No), /* N */
+ M(Sm) | M(Sc) | M(Sk) | M(So), /* S */
+ M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */
+ M(Zs) | M(Zl) | M(Zp), /* Z */
+ M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */
+};
+
+/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2
+ if not found */
+int unicode_general_category(CharRange *cr, const char *gc_name)
+{
+ int gc_idx;
+ uint32_t gc_mask;
+
+ gc_idx = unicode_find_name(unicode_gc_name_table, gc_name);
+ if (gc_idx < 0)
+ return -2;
+ if (gc_idx <= UNICODE_GC_Co) {
+ gc_mask = (uint64_t)1 << gc_idx;
+ } else {
+ gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC];
+ }
+ return unicode_general_category1(cr, gc_mask);
+}
+
+
+/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2
+ if not found */
+int unicode_prop(CharRange *cr, const char *prop_name)
+{
+ int prop_idx, ret;
+
+ prop_idx = unicode_find_name(unicode_prop_name_table, prop_name);
+ if (prop_idx < 0)
+ return -2;
+ prop_idx += UNICODE_PROP_ASCII_Hex_Digit;
+
+ ret = 0;
+ switch(prop_idx) {
+ case UNICODE_PROP_ASCII:
+ if (cr_add_interval(cr, 0x00, 0x7f + 1))
+ return -1;
+ break;
+ case UNICODE_PROP_Any:
+ if (cr_add_interval(cr, 0x00000, 0x10ffff + 1))
+ return -1;
+ break;
+ case UNICODE_PROP_Assigned:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Cn),
+ POP_INVERT,
+ POP_END);
+ break;
+ case UNICODE_PROP_Math:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Sm),
+ POP_PROP, UNICODE_PROP_Other_Math,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Lowercase:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Ll),
+ POP_PROP, UNICODE_PROP_Other_Lowercase,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Uppercase:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu),
+ POP_PROP, UNICODE_PROP_Other_Uppercase,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Cased:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt),
+ POP_PROP, UNICODE_PROP_Other_Uppercase,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_Lowercase,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Alphabetic:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),
+ POP_PROP, UNICODE_PROP_Other_Uppercase,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_Lowercase,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_Alphabetic,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Grapheme_Base:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn),
+ POP_PROP, UNICODE_PROP_Other_Grapheme_Extend,
+ POP_UNION,
+ POP_INVERT,
+ POP_END);
+ break;
+ case UNICODE_PROP_Grapheme_Extend:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Me) | M(Mn),
+ POP_PROP, UNICODE_PROP_Other_Grapheme_Extend,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_XID_Start:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),
+ POP_PROP, UNICODE_PROP_Other_ID_Start,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Pattern_Syntax,
+ POP_PROP, UNICODE_PROP_Pattern_White_Space,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_XID_Start1,
+ POP_UNION,
+ POP_INVERT,
+ POP_INTER,
+ POP_END);
+ break;
+ case UNICODE_PROP_XID_Continue:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) |
+ M(Mn) | M(Mc) | M(Nd) | M(Pc),
+ POP_PROP, UNICODE_PROP_Other_ID_Start,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_ID_Continue,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Pattern_Syntax,
+ POP_PROP, UNICODE_PROP_Pattern_White_Space,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_XID_Continue1,
+ POP_UNION,
+ POP_INVERT,
+ POP_INTER,
+ POP_END);
+ break;
+ case UNICODE_PROP_Changes_When_Uppercased:
+ ret = unicode_case1(cr, CASE_U);
+ break;
+ case UNICODE_PROP_Changes_When_Lowercased:
+ ret = unicode_case1(cr, CASE_L);
+ break;
+ case UNICODE_PROP_Changes_When_Casemapped:
+ ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F);
+ break;
+ case UNICODE_PROP_Changes_When_Titlecased:
+ ret = unicode_prop_ops(cr,
+ POP_CASE, CASE_U,
+ POP_PROP, UNICODE_PROP_Changes_When_Titlecased1,
+ POP_XOR,
+ POP_END);
+ break;
+ case UNICODE_PROP_Changes_When_Casefolded:
+ ret = unicode_prop_ops(cr,
+ POP_CASE, CASE_F,
+ POP_PROP, UNICODE_PROP_Changes_When_Casefolded1,
+ POP_XOR,
+ POP_END);
+ break;
+ case UNICODE_PROP_Changes_When_NFKC_Casefolded:
+ ret = unicode_prop_ops(cr,
+ POP_CASE, CASE_F,
+ POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1,
+ POP_XOR,
+ POP_END);
+ break;
+#if 0
+ case UNICODE_PROP_ID_Start:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),
+ POP_PROP, UNICODE_PROP_Other_ID_Start,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Pattern_Syntax,
+ POP_PROP, UNICODE_PROP_Pattern_White_Space,
+ POP_UNION,
+ POP_INVERT,
+ POP_INTER,
+ POP_END);
+ break;
+ case UNICODE_PROP_ID_Continue:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) |
+ M(Mn) | M(Mc) | M(Nd) | M(Pc),
+ POP_PROP, UNICODE_PROP_Other_ID_Start,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_ID_Continue,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Pattern_Syntax,
+ POP_PROP, UNICODE_PROP_Pattern_White_Space,
+ POP_UNION,
+ POP_INVERT,
+ POP_INTER,
+ POP_END);
+ break;
+ case UNICODE_PROP_Case_Ignorable:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Mn) | M(Cf) | M(Lm) | M(Sk),
+ POP_PROP, UNICODE_PROP_Case_Ignorable1,
+ POP_XOR,
+ POP_END);
+ break;
+#else
+ /* we use the existing tables */
+ case UNICODE_PROP_ID_Continue:
+ ret = unicode_prop_ops(cr,
+ POP_PROP, UNICODE_PROP_ID_Start,
+ POP_PROP, UNICODE_PROP_ID_Continue1,
+ POP_XOR,
+ POP_END);
+ break;
+#endif
+ default:
+ if (prop_idx >= countof(unicode_prop_table))
+ return -2;
+ ret = unicode_prop1(cr, prop_idx);
+ break;
+ }
+ return ret;
+}
+
+#endif /* CONFIG_ALL_UNICODE */
diff --git a/src/shared/quickjs/libunicode.h b/src/shared/quickjs/libunicode.h
new file mode 100644
index 000000000..cfa600a50
--- /dev/null
+++ b/src/shared/quickjs/libunicode.h
@@ -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 */
diff --git a/src/shared/quickjs/list.h b/src/shared/quickjs/list.h
new file mode 100644
index 000000000..e7f51a9d9
--- /dev/null
+++ b/src/shared/quickjs/list.h
@@ -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_impl(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_impl(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_impl(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 */
diff --git a/src/shared/quickjs/quickjs-atom.h b/src/shared/quickjs/quickjs-atom.h
new file mode 100644
index 000000000..4c2279452
--- /dev/null
+++ b/src/shared/quickjs/quickjs-atom.h
@@ -0,0 +1,273 @@
+/*
+ * 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(errors, "errors")
+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(_arg_var_, "<arg_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(computed_field, "<computed_field>")
+DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */
+DEF(class_fields_init, "<class_fields_init>")
+DEF(brand, "<brand>")
+DEF(hash_constructor, "#constructor")
+DEF(as, "as")
+DEF(from, "from")
+DEF(meta, "meta")
+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")
+DEF(status, "status")
+DEF(reason, "reason")
+DEF(globalThis, "globalThis")
+#ifdef CONFIG_BIGNUM
+DEF(bigint, "bigint")
+DEF(bigfloat, "bigfloat")
+DEF(bigdecimal, "bigdecimal")
+DEF(roundingMode, "roundingMode")
+DEF(maximumSignificantDigits, "maximumSignificantDigits")
+DEF(maximumFractionDigits, "maximumFractionDigits")
+#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")
+DEF(BigDecimal, "BigDecimal")
+DEF(OperatorSet, "OperatorSet")
+DEF(Operators, "Operators")
+#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(RegExp_String_Iterator, "RegExp 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")
+/* private symbols */
+DEF(Private_brand, "<brand>")
+/* symbols */
+DEF(Symbol_toPrimitive, "Symbol.toPrimitive")
+DEF(Symbol_iterator, "Symbol.iterator")
+DEF(Symbol_match, "Symbol.match")
+DEF(Symbol_matchAll, "Symbol.matchAll")
+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_operatorSet, "Symbol.operatorSet")
+#endif
+
+#endif /* DEF */
diff --git a/src/shared/quickjs/quickjs-opcode.h b/src/shared/quickjs/quickjs-opcode.h
new file mode 100644
index 000000000..c731a14a9
--- /dev/null
+++ b/src/shared/quickjs/quickjs-opcode.h
@@ -0,0 +1,365 @@
+/*
+ * 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(npop_u16)
+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( private_symbol, 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( special_object, 2, 0, 1, u8) /* only used at the start of a function */
+DEF( rest, 3, 0, 1, u16) /* 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( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
+DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
+DEF( return_async, 1, 1, 0, none)
+DEF( throw, 1, 1, 0, none)
+DEF( throw_error, 6, 0, 0, atom_u8)
+DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
+DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
+DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
+ bytecode string */
+DEF( get_super, 1, 1, 1, none)
+DEF( import, 1, 1, 1, none) /* dynamic module import */
+
+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_private_field, 1, 2, 1, none) /* obj prop -> value */
+DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
+DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
+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(set_home_object, 1, 2, 2, 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, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */
+DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */
+
+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( 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( 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( 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( 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( 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(iterator_check_object, 1, 1, 1, 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( iterator_next, 1, 4, 4, none)
+DEF( iterator_call, 2, 4, 5, u8)
+DEF( initial_yield, 1, 0, 0, none)
+DEF( yield, 1, 1, 2, none)
+DEF( yield_star, 1, 1, 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)
+DEF(is_undefined_or_null, 1, 1, 1, none)
+#ifdef CONFIG_BIGNUM
+DEF( mul_pow10, 1, 2, 1, none)
+DEF( math_mod, 1, 2, 1, none)
+#endif
+/* must be the last non short and non temporary opcode */
+DEF( nop, 1, 0, 0, none)
+
+/* temporary opcodes: never emitted in the final bytecode */
+
+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( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
+
+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(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
+def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
+def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
+
+def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
+
+def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
+
+#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(typeof_is_undefined, 1, 1, 1, none)
+DEF( typeof_is_function, 1, 1, 1, none)
+#endif
+
+#undef DEF
+#undef def
+#endif /* DEF */
diff --git a/src/shared/quickjs/quickjs.c b/src/shared/quickjs/quickjs.c
new file mode 100644
index 000000000..f90fb9e4f
--- /dev/null
+++ b/src/shared/quickjs/quickjs.c
@@ -0,0 +1,54315 @@
+/*
+ * QuickJS Javascript Engine
+ *
+ * Copyright (c) 2017-2021 Fabrice Bellard
+ * Copyright (c) 2017-2021 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>
+#ifndef _MSC_VER
+#include <sys/time.h>
+#endif
+#include <time.h>
+#include <fenv.h>
+#include <math.h>
+#if defined(__APPLE__)
+#include <malloc/malloc.h>
+#elif defined(__linux__)
+#include <malloc.h>
+#elif defined(__FreeBSD__)
+#include <malloc_np.h>
+#endif
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#include <windows.h>
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+ || defined(__APPLE__)
+#include <xlocale.h>
+#else
+#include <locale.h>
+#endif
+
+#include "cutils.h"
+#include "list.h"
+#include "quickjs.h"
+#include "libregexp.h"
+#ifdef CONFIG_BIGNUM
+#include "libbf.h"
+#endif
+
+#define OPTIMIZE 0
+#define SHORT_OPCODES 1
+#if defined(EMSCRIPTEN) || defined(_MSC_VER)
+#define DIRECT_DISPATCH 0
+#else
+#define DIRECT_DISPATCH 1
+#endif
+
+#if defined(__APPLE__)
+#define MALLOC_OVERHEAD 0
+#else
+#define MALLOC_OVERHEAD 8
+#endif
+
+#if !defined(_WIN32)
+/* define it if printf uses the RNDN rounding mode instead of RNDNA */
+#define CONFIG_PRINTF_RNDN
+#endif
+
+/* define to include Atomics.* operations which depend on the OS
+ threads */
+#if !defined(EMSCRIPTEN) && !defined(_MSC_VER)
+#define CONFIG_ATOMICS
+#endif
+
+#if !defined(EMSCRIPTEN)
+/* enable stack limitation */
+#define CONFIG_STACK_CHECK
+#endif
+
+/* dump object free */
+//#define DUMP_FREE
+//#define DUMP_CLOSURE
+/* dump the bytecode of the compiled functions: combination of bits
+ 1: dump pass 3 final byte code
+ 2: dump pass 2 code
+ 4: dump pass 1 code
+ 8: dump stdlib functions
+ 16: dump bytecode in hex
+ 32: dump line number table
+ */
+//#define DUMP_BYTECODE (1)
+/* dump the occurence of the automatic GC */
+//#define DUMP_GC
+/* dump objects freed by the garbage collector */
+//#define DUMP_GC_FREE
+/* dump objects leaking when freeing the runtime */
+//#define DUMP_LEAKS 1
+/* dump memory usage before running the garbage collector */
+//#define DUMP_MEM
+//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */
+//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */
+//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */
+//#define DUMP_MODULE_RESOLVE
+//#define DUMP_PROMISE
+//#define DUMP_READ_OBJECT
+
+/* test the GC by forcing it before each object allocation */
+//#define FORCE_GC_AT_MALLOC
+
+#ifdef CONFIG_ATOMICS
+#include <pthread.h>
+#include <stdatomic.h>
+#include <errno.h>
+#endif
+
+static double safe_strtod(const char *restrict nptr, char **restrict endptr)
+{
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+ setlocale(LC_NUMERIC, "C");
+#else
+ const locale_t tempLoc = newlocale(LC_NUMERIC_MASK, "C", 0);
+ uselocale(tempLoc);
+#endif
+ double d = strtod(nptr, endptr);
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ _configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
+#else
+ uselocale(LC_GLOBAL_LOCALE);
+ freelocale(tempLoc);
+#endif
+ return d;
+}
+
+enum {
+ /* classid tag */ /* union usage | properties */
+ JS_CLASS_OBJECT = 1, /* must be first */
+ JS_CLASS_ARRAY, /* u.array | length */
+ JS_CLASS_ERROR,
+ JS_CLASS_NUMBER, /* u.object_data */
+ JS_CLASS_STRING, /* u.object_data */
+ JS_CLASS_BOOLEAN, /* u.object_data */
+ JS_CLASS_SYMBOL, /* u.object_data */
+ JS_CLASS_ARGUMENTS, /* u.array | length */
+ JS_CLASS_MAPPED_ARGUMENTS, /* | length */
+ JS_CLASS_DATE, /* u.object_data */
+ JS_CLASS_MODULE_NS,
+ JS_CLASS_C_FUNCTION, /* u.cfunc */
+ JS_CLASS_BYTECODE_FUNCTION, /* u.func */
+ JS_CLASS_BOUND_FUNCTION, /* u.bound_function */
+ JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */
+ JS_CLASS_GENERATOR_FUNCTION, /* u.func */
+ JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */
+ JS_CLASS_REGEXP, /* u.regexp */
+ JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */
+ JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */
+ JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */
+#ifdef CONFIG_BIGNUM
+ JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */
+#endif
+ JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_DATAVIEW, /* u.typed_array */
+#ifdef CONFIG_BIGNUM
+ JS_CLASS_BIG_INT, /* u.object_data */
+ JS_CLASS_BIG_FLOAT, /* u.object_data */
+ JS_CLASS_FLOAT_ENV, /* u.float_env */
+ JS_CLASS_BIG_DECIMAL, /* u.object_data */
+ JS_CLASS_OPERATOR_SET, /* u.operator_set */
+#endif
+ JS_CLASS_MAP, /* u.map_state */
+ JS_CLASS_SET, /* u.map_state */
+ JS_CLASS_WEAKMAP, /* u.map_state */
+ JS_CLASS_WEAKSET, /* u.map_state */
+ JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */
+ JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */
+ JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */
+ JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */
+ JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */
+ JS_CLASS_GENERATOR, /* u.generator_data */
+ JS_CLASS_PROXY, /* u.proxy_data */
+ JS_CLASS_PROMISE, /* u.promise_data */
+ JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */
+ JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */
+ JS_CLASS_ASYNC_FUNCTION, /* u.func */
+ JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */
+ JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */
+ JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
+ JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
+ JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
+
+ JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
+};
+
+/* number of typed array types */
+#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)
+static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];
+#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])
+
+typedef enum JSErrorEnum {
+ JS_EVAL_ERROR,
+ JS_RANGE_ERROR,
+ JS_REFERENCE_ERROR,
+ JS_SYNTAX_ERROR,
+ JS_TYPE_ERROR,
+ JS_URI_ERROR,
+ JS_INTERNAL_ERROR,
+ JS_AGGREGATE_ERROR,
+
+ JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
+} JSErrorEnum;
+
+#define JS_MAX_LOCAL_VARS 65536
+#define JS_STACK_SIZE_MAX 65534
+#define JS_STRING_LEN_MAX ((1 << 30) - 1)
+
+#ifdef __GNUC__
+#define warn_unused __attribute__((warn_unused_result))
+#else
+#define warn_unused
+#endif
+
+typedef struct JSShape JSShape;
+typedef struct JSString JSString;
+typedef struct JSString JSAtomStruct;
+
+typedef enum {
+ JS_GC_PHASE_NONE,
+ JS_GC_PHASE_DECREF,
+ JS_GC_PHASE_REMOVE_CYCLES,
+} JSGCPhaseEnum;
+
+typedef enum OPCodeEnum OPCodeEnum;
+
+#ifdef CONFIG_BIGNUM
+/* function pointers are used for numeric operations so that it is
+ possible to remove some numeric types */
+typedef struct {
+ JSValue (*to_string)(JSContext *ctx, JSValueConst val);
+ JSValue (*from_string)(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent);
+ int (*unary_arith)(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1);
+ int (*binary_arith)(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2);
+ int (*compare)(JSContext *ctx, OPCodeEnum op,
+ JSValue op1, JSValue op2);
+ /* only for bigfloat: */
+ JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,
+ int64_t exponent);
+ int (*mul_pow10)(JSContext *ctx, JSValue *sp);
+} JSNumericOperations;
+#endif
+
+struct JSRuntime {
+ JSMallocFunctions mf;
+ JSMallocState malloc_state;
+ const char *rt_info;
+
+ int atom_hash_size; /* power of two */
+ int atom_count;
+ int atom_size;
+ int atom_count_resize; /* resize hash table at this count */
+ uint32_t *atom_hash;
+ JSAtomStruct **atom_array;
+ int atom_free_index; /* 0 = none */
+
+ int class_count; /* size of class_array */
+ JSClass *class_array;
+
+ struct list_head context_list; /* list of JSContext.link */
+ /* list of JSGCObjectHeader.link. List of allocated GC objects (used
+ by the garbage collector) */
+ struct list_head gc_obj_list;
+ /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
+ struct list_head gc_zero_ref_count_list;
+ struct list_head tmp_obj_list; /* used during GC */
+ JSGCPhaseEnum gc_phase : 8;
+ size_t malloc_gc_threshold;
+#ifdef DUMP_LEAKS
+ struct list_head string_list; /* list of JSString.link */
+#endif
+ /* stack limitation */
+ uintptr_t stack_size; /* in bytes, 0 if no limit */
+ uintptr_t stack_top;
+ uintptr_t stack_limit; /* lower stack limit */
+
+ JSValue current_exception;
+ /* true if inside an out of memory error, to avoid recursing */
+ BOOL in_out_of_memory : 8;
+
+ struct JSStackFrame *current_stack_frame;
+
+ JSInterruptHandler *interrupt_handler;
+ void *interrupt_opaque;
+
+ JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
+ void *host_promise_rejection_tracker_opaque;
+
+ struct list_head job_list; /* list of JSJobEntry.link */
+
+ JSModuleNormalizeFunc *module_normalize_func;
+ JSModuleLoaderFunc *module_loader_func;
+ void *module_loader_opaque;
+
+ BOOL can_block : 8; /* TRUE if Atomics.wait can block */
+ /* used to allocate, free and clone SharedArrayBuffers */
+ JSSharedArrayBufferFunctions sab_funcs;
+
+ /* Shape hash table */
+ int shape_hash_bits;
+ int shape_hash_size;
+ int shape_hash_count; /* number of hashed shapes */
+ JSShape **shape_hash;
+#ifdef CONFIG_BIGNUM
+ bf_context_t bf_ctx;
+ JSNumericOperations bigint_ops;
+ JSNumericOperations bigfloat_ops;
+ JSNumericOperations bigdecimal_ops;
+ uint32_t operator_count;
+#endif
+ void *user_opaque;
+};
+
+struct JSClass {
+ uint32_t class_id; /* 0 means free entry */
+ JSAtom class_name;
+ JSClassFinalizer *finalizer;
+ JSClassGCMark *gc_mark;
+ JSClassCall *call;
+ /* pointers for exotic behavior, can be NULL if none are present */
+ const JSClassExoticMethods *exotic;
+};
+
+#define JS_MODE_STRICT (1 << 0)
+#define JS_MODE_STRIP (1 << 1)
+#define JS_MODE_MATH (1 << 2)
+
+typedef struct JSStackFrame {
+ struct JSStackFrame *prev_frame; /* NULL if first stack frame */
+ JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
+ JSValue *arg_buf; /* arguments */
+ JSValue *var_buf; /* variables */
+ struct list_head var_ref_list; /* list of JSVarRef.link */
+ const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
+ instruction after the call */
+ int arg_count;
+ int js_mode; /* 0 or JS_MODE_MATH for C functions */
+ /* only used in generators. Current stack pointer value. NULL if
+ the function is running. */
+ JSValue *cur_sp;
+} JSStackFrame;
+
+typedef enum {
+ JS_GC_OBJ_TYPE_JS_OBJECT,
+ JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
+ JS_GC_OBJ_TYPE_SHAPE,
+ JS_GC_OBJ_TYPE_VAR_REF,
+ JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
+ JS_GC_OBJ_TYPE_JS_CONTEXT,
+} JSGCObjectTypeEnum;
+
+/* header for GC objects. GC objects are C data structures with a
+ reference count that can reference other GC objects. JS Objects are
+ a particular type of GC object. */
+struct JSGCObjectHeader {
+ int ref_count; /* must come first, 32-bit */
+ JSGCObjectTypeEnum gc_obj_type : 4;
+ uint8_t mark : 4; /* used by the GC */
+ uint8_t dummy1; /* not used by the GC */
+ uint16_t dummy2; /* not used by the GC */
+ struct list_head link;
+};
+
+typedef struct JSVarRef {
+ union {
+ JSGCObjectHeader header; /* must come first */
+ struct {
+ int _gc_ref_count; /* corresponds to header.ref_count */
+ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */
+
+ /* 0 : the JSVarRef is on the stack. header.link is an element
+ of JSStackFrame.var_ref_list.
+ 1 : the JSVarRef is detached. header.link has the normal meanning
+ */
+ uint8_t is_detached : 1;
+ uint8_t is_arg : 1;
+ uint16_t var_idx; /* index of the corresponding function variable on
+ the stack */
+ };
+ };
+ JSValue *pvalue; /* pointer to the value, either on the stack or
+ to 'value' */
+ JSValue value; /* used when the variable is no longer on the stack */
+} JSVarRef;
+
+#ifdef CONFIG_BIGNUM
+typedef struct JSFloatEnv {
+ limb_t prec;
+ bf_flags_t flags;
+ unsigned int status;
+} JSFloatEnv;
+
+/* the same structure is used for big integers and big floats. Big
+ integers are never infinite or NaNs */
+typedef struct JSBigFloat {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ bf_t num;
+} JSBigFloat;
+
+typedef struct JSBigDecimal {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ bfdec_t num;
+} JSBigDecimal;
+#endif
+
+typedef enum {
+ JS_AUTOINIT_ID_PROTOTYPE,
+ JS_AUTOINIT_ID_MODULE_NS,
+ JS_AUTOINIT_ID_PROP,
+} JSAutoInitIDEnum;
+
+/* must be large enough to have a negligible runtime cost and small
+ enough to call the interrupt callback often. */
+#define JS_INTERRUPT_COUNTER_INIT 10000
+
+struct JSContext {
+ JSGCObjectHeader header; /* must come first */
+ JSRuntime *rt;
+ struct list_head link;
+
+ uint16_t binary_object_count;
+ int binary_object_size;
+
+ JSShape *array_shape; /* initial shape for Array objects */
+
+ JSValue *class_proto;
+ JSValue function_proto;
+ JSValue function_ctor;
+ JSValue array_ctor;
+ JSValue regexp_ctor;
+ JSValue promise_ctor;
+ JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
+ JSValue iterator_proto;
+ JSValue async_iterator_proto;
+ JSValue array_proto_values;
+ JSValue throw_type_error;
+ JSValue eval_obj;
+
+ JSValue global_obj; /* global object */
+ JSValue global_var_obj; /* contains the global let/const definitions */
+
+ uint64_t random_state;
+#ifdef CONFIG_BIGNUM
+ bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */
+ JSFloatEnv fp_env; /* global FP environment */
+ BOOL bignum_ext : 8; /* enable math mode */
+ BOOL allow_operator_overloading : 8;
+#endif
+ /* when the counter reaches zero, JSRutime.interrupt_handler is called */
+ int interrupt_counter;
+ BOOL is_error_property_enabled;
+
+ struct list_head loaded_modules; /* list of JSModuleDef.link */
+
+ /* if NULL, RegExp compilation is not supported */
+ JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,
+ JSValueConst flags);
+ /* if NULL, eval is not supported */
+ JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int flags, int scope_idx);
+ void *user_opaque;
+ ScopeLookup *scopeLookup;
+ FoundUndefinedHandler *handleUndefined;
+ FunctionEnteredHandler *handleFunctionEntered;
+ FunctionExitedHandler *handleFunctionExited;
+};
+
+typedef union JSFloat64Union {
+ double d;
+ uint64_t u64;
+ uint32_t u32[2];
+} JSFloat64Union;
+
+enum {
+ JS_ATOM_TYPE_STRING = 1,
+ JS_ATOM_TYPE_GLOBAL_SYMBOL,
+ JS_ATOM_TYPE_SYMBOL,
+ JS_ATOM_TYPE_PRIVATE,
+};
+
+enum {
+ JS_ATOM_HASH_SYMBOL,
+ JS_ATOM_HASH_PRIVATE,
+};
+
+typedef enum {
+ JS_ATOM_KIND_STRING,
+ JS_ATOM_KIND_SYMBOL,
+ JS_ATOM_KIND_PRIVATE,
+} JSAtomKindEnum;
+
+#define JS_ATOM_HASH_MASK ((1 << 30) - 1)
+
+struct JSString {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ uint32_t len : 31;
+ uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */
+ /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,
+ for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3
+ XXX: could change encoding to have one more bit in hash */
+ uint32_t hash : 30;
+ uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
+ uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
+#ifdef DUMP_LEAKS
+ struct list_head link; /* string list */
+#endif
+ union {
+ uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */
+ uint16_t str16[0];
+ } u;
+};
+
+typedef struct JSClosureVar {
+ uint8_t is_local : 1;
+ uint8_t is_arg : 1;
+ uint8_t is_const : 1;
+ uint8_t is_lexical : 1;
+ uint8_t var_kind : 4; /* see JSVarKindEnum */
+ /* 8 bits available */
+ uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
+ parent function. otherwise: index to a closure
+ variable of the parent function */
+ JSAtom var_name;
+} JSClosureVar;
+
+#define ARG_SCOPE_INDEX 1
+#define ARG_SCOPE_END (-2)
+
+typedef struct JSVarScope {
+ int parent; /* index into fd->scopes of the enclosing scope */
+ int first; /* index into fd->vars of the last variable in this scope */
+} JSVarScope;
+
+typedef enum {
+ /* XXX: add more variable kinds here instead of using bit fields */
+ JS_VAR_NORMAL,
+ JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */
+ JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator
+ function declaration */
+ JS_VAR_CATCH,
+ JS_VAR_FUNCTION_NAME, /* function expression name */
+ JS_VAR_PRIVATE_FIELD,
+ JS_VAR_PRIVATE_METHOD,
+ JS_VAR_PRIVATE_GETTER,
+ JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
+ JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
+} JSVarKindEnum;
+
+/* XXX: could use a different structure in bytecode functions to save
+ memory */
+typedef struct JSVarDef {
+ JSAtom var_name;
+ /* index into fd->scopes of this variable lexical scope */
+ int scope_level;
+ /* during compilation:
+ - if scope_level = 0: scope in which the variable is defined
+ - if scope_level != 0: index into fd->vars of the next
+ variable in the same or enclosing lexical scope
+ in a bytecode function:
+ index into fd->vars of the next
+ variable in the same or enclosing lexical scope
+ */
+ int scope_next;
+ uint8_t is_const : 1;
+ uint8_t is_lexical : 1;
+ uint8_t is_captured : 1;
+ uint8_t var_kind : 4; /* see JSVarKindEnum */
+ /* only used during compilation: function pool index for lexical
+ variables with var_kind =
+ JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of
+ the definition of the 'var' variables (they have scope_level =
+ 0) */
+ int func_pool_idx : 24; /* only used during compilation : index in
+ the constant pool for hoisted function
+ definition */
+} JSVarDef;
+
+/* for the encoding of the pc2line table */
+#define PC2LINE_BASE (-1)
+#define PC2LINE_RANGE 5
+#define PC2LINE_OP_FIRST 1
+#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)
+
+typedef enum JSFunctionKindEnum {
+ JS_FUNC_NORMAL = 0,
+ JS_FUNC_GENERATOR = (1 << 0),
+ JS_FUNC_ASYNC = (1 << 1),
+ JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),
+} JSFunctionKindEnum;
+
+typedef struct JSFunctionBytecode {
+ JSGCObjectHeader header; /* must come first */
+ uint8_t js_mode;
+ uint8_t has_prototype : 1; /* true if a prototype field is necessary */
+ uint8_t has_simple_parameter_list : 1;
+ uint8_t is_derived_class_constructor : 1;
+ /* true if home_object needs to be initialized */
+ uint8_t need_home_object : 1;
+ uint8_t func_kind : 2;
+ uint8_t new_target_allowed : 1;
+ uint8_t super_call_allowed : 1;
+ uint8_t super_allowed : 1;
+ uint8_t arguments_allowed : 1;
+ uint8_t has_debug : 1;
+ uint8_t backtrace_barrier : 1; /* stop backtrace on this function */
+ uint8_t read_only_bytecode : 1;
+ /* XXX: 4 bits available */
+ uint8_t *byte_code_buf; /* (self pointer) */
+ int byte_code_len;
+ JSAtom func_name;
+ JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */
+ JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */
+ uint16_t arg_count;
+ uint16_t var_count;
+ uint16_t defined_arg_count; /* for length function property */
+ uint16_t stack_size; /* maximum stack size */
+ JSContext *realm; /* function realm */
+ JSValue *cpool; /* constant pool (self pointer) */
+ int cpool_count;
+ int closure_var_count;
+ struct {
+ /* debug info, move to separate structure to save memory? */
+ JSAtom filename;
+ int line_num;
+ int source_len;
+ int pc2line_len;
+ uint8_t *pc2line_buf;
+ char *source;
+ } debug;
+} JSFunctionBytecode;
+
+typedef struct JSBoundFunction {
+ JSValue func_obj;
+ JSValue this_val;
+ int argc;
+ JSValue argv[0];
+} JSBoundFunction;
+
+typedef enum JSIteratorKindEnum {
+ JS_ITERATOR_KIND_KEY,
+ JS_ITERATOR_KIND_VALUE,
+ JS_ITERATOR_KIND_KEY_AND_VALUE,
+} JSIteratorKindEnum;
+
+typedef struct JSForInIterator {
+ JSValue obj;
+ BOOL is_array;
+ uint32_t array_length;
+ uint32_t idx;
+} JSForInIterator;
+
+typedef struct JSRegExp {
+ JSString *pattern;
+ JSString *bytecode; /* also contains the flags */
+} JSRegExp;
+
+typedef struct JSProxyData {
+ JSValue target;
+ JSValue handler;
+ uint8_t is_func;
+ uint8_t is_revoked;
+} JSProxyData;
+
+typedef struct JSArrayBuffer {
+ int byte_length; /* 0 if detached */
+ uint8_t detached;
+ uint8_t shared; /* if shared, the array buffer cannot be detached */
+ uint8_t *data; /* NULL if detached */
+ struct list_head array_list;
+ void *opaque;
+ JSFreeArrayBufferDataFunc *free_func;
+} JSArrayBuffer;
+
+typedef struct JSTypedArray {
+ struct list_head link; /* link to arraybuffer */
+ JSObject *obj; /* back pointer to the TypedArray/DataView object */
+ JSObject *buffer; /* based array buffer */
+ uint32_t offset; /* offset in the array buffer */
+ uint32_t length; /* length in the array buffer */
+} JSTypedArray;
+
+typedef struct JSAsyncFunctionState {
+ JSValue this_val; /* 'this' generator argument */
+ int argc; /* number of function arguments */
+ BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
+ JSStackFrame frame;
+} JSAsyncFunctionState;
+
+/* XXX: could use an object instead to avoid the
+ JS_TAG_ASYNC_FUNCTION tag for the GC */
+typedef struct JSAsyncFunctionData {
+ JSGCObjectHeader header; /* must come first */
+ JSValue resolving_funcs[2];
+ BOOL is_active; /* true if the async function state is valid */
+ JSAsyncFunctionState func_state;
+} JSAsyncFunctionData;
+
+typedef enum {
+ /* binary operators */
+ JS_OVOP_ADD,
+ JS_OVOP_SUB,
+ JS_OVOP_MUL,
+ JS_OVOP_DIV,
+ JS_OVOP_MOD,
+ JS_OVOP_POW,
+ JS_OVOP_OR,
+ JS_OVOP_AND,
+ JS_OVOP_XOR,
+ JS_OVOP_SHL,
+ JS_OVOP_SAR,
+ JS_OVOP_SHR,
+ JS_OVOP_EQ,
+ JS_OVOP_LESS,
+
+ JS_OVOP_BINARY_COUNT,
+ /* unary operators */
+ JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
+ JS_OVOP_NEG,
+ JS_OVOP_INC,
+ JS_OVOP_DEC,
+ JS_OVOP_NOT,
+
+ JS_OVOP_COUNT,
+} JSOverloadableOperatorEnum;
+
+typedef struct {
+ uint32_t operator_index;
+ JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
+} JSBinaryOperatorDefEntry;
+
+typedef struct {
+ int count;
+ JSBinaryOperatorDefEntry *tab;
+} JSBinaryOperatorDef;
+
+typedef struct {
+ uint32_t operator_counter;
+ BOOL is_primitive; /* OperatorSet for a primitive type */
+ /* NULL if no operator is defined */
+ JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
+ JSBinaryOperatorDef left;
+ JSBinaryOperatorDef right;
+} JSOperatorSetData;
+
+typedef struct JSReqModuleEntry {
+ JSAtom module_name;
+ JSModuleDef *module; /* used using resolution */
+} JSReqModuleEntry;
+
+typedef enum JSExportTypeEnum {
+ JS_EXPORT_TYPE_LOCAL,
+ JS_EXPORT_TYPE_INDIRECT,
+} JSExportTypeEnum;
+
+typedef struct JSExportEntry {
+ union {
+ struct {
+ int var_idx; /* closure variable index */
+ JSVarRef *var_ref; /* if != NULL, reference to the variable */
+ } local; /* for local export */
+ int req_module_idx; /* module for indirect export */
+ } u;
+ JSExportTypeEnum export_type;
+ JSAtom local_name; /* '*' if export ns from. not used for local
+ export after compilation */
+ JSAtom export_name; /* exported variable name */
+} JSExportEntry;
+
+typedef struct JSStarExportEntry {
+ int req_module_idx; /* in req_module_entries */
+} JSStarExportEntry;
+
+typedef struct JSImportEntry {
+ int var_idx; /* closure variable index */
+ JSAtom import_name;
+ int req_module_idx; /* in req_module_entries */
+} JSImportEntry;
+
+struct JSModuleDef {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ JSAtom module_name;
+ struct list_head link;
+
+ JSReqModuleEntry *req_module_entries;
+ int req_module_entries_count;
+ int req_module_entries_size;
+
+ JSExportEntry *export_entries;
+ int export_entries_count;
+ int export_entries_size;
+
+ JSStarExportEntry *star_export_entries;
+ int star_export_entries_count;
+ int star_export_entries_size;
+
+ JSImportEntry *import_entries;
+ int import_entries_count;
+ int import_entries_size;
+
+ JSValue module_ns;
+ JSValue func_obj; /* only used for JS modules */
+ JSModuleInitFunc *init_func; /* only used for C modules */
+ BOOL resolved : 8;
+ BOOL func_created : 8;
+ BOOL instantiated : 8;
+ BOOL evaluated : 8;
+ BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */
+ /* true if evaluation yielded an exception. It is saved in
+ eval_exception */
+ BOOL eval_has_exception : 8;
+ JSValue eval_exception;
+ JSValue meta_obj; /* for import.meta */
+};
+
+typedef struct JSJobEntry {
+ struct list_head link;
+ JSContext *ctx;
+ JSJobFunc *job_func;
+ int argc;
+ JSValue argv[0];
+} JSJobEntry;
+
+typedef struct JSProperty {
+ union {
+ JSValue value; /* JS_PROP_NORMAL */
+ struct { /* JS_PROP_GETSET */
+ JSObject *getter; /* NULL if undefined */
+ JSObject *setter; /* NULL if undefined */
+ } getset;
+ JSVarRef *var_ref; /* JS_PROP_VARREF */
+ struct { /* JS_PROP_AUTOINIT */
+ /* in order to use only 2 pointers, we compress the realm
+ and the init function pointer */
+ uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
+ in the 2 low bits */
+ void *opaque;
+ } init;
+ } u;
+} JSProperty;
+
+#define JS_PROP_INITIAL_SIZE 2
+#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
+#define JS_ARRAY_INITIAL_SIZE 2
+
+typedef struct JSShapeProperty {
+ uint32_t hash_next : 26; /* 0 if last in list */
+ uint32_t flags : 6; /* JS_PROP_XXX */
+ JSAtom atom; /* JS_ATOM_NULL = free property entry */
+} JSShapeProperty;
+
+struct JSShape {
+ /* hash table of size hash_mask + 1 before the start of the
+ structure (see prop_hash_end()). */
+ JSGCObjectHeader header;
+ /* true if the shape is inserted in the shape hash table. If not,
+ JSShape.hash is not valid */
+ uint8_t is_hashed;
+ /* If true, the shape may have small array index properties 'n' with 0
+ <= n <= 2^31-1. If false, the shape is guaranteed not to have
+ small array index properties */
+ uint8_t has_small_array_index;
+ uint32_t hash; /* current hash value */
+ uint32_t prop_hash_mask;
+ int prop_size; /* allocated properties */
+ int prop_count; /* include deleted properties */
+ int deleted_prop_count;
+ JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
+ JSObject *proto;
+ JSShapeProperty prop[0]; /* prop_size elements */
+};
+
+struct JSObject {
+ union {
+ JSGCObjectHeader header;
+ struct {
+ int _gc_ref_count; /* corresponds to header.ref_count */
+ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */
+
+ uint8_t extensible : 1;
+ uint8_t free_mark : 1; /* only used when freeing objects with cycles */
+ uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */
+ uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */
+ uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
+ uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */
+ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
+ uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
+ uint16_t class_id; /* see JS_CLASS_x */
+ };
+ };
+ /* byte offsets: 16/24 */
+ JSShape *shape; /* prototype and property names + flag */
+ JSProperty *prop; /* array of properties */
+ /* byte offsets: 24/40 */
+ struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */
+ /* byte offsets: 28/48 */
+ union {
+ void *opaque;
+ struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
+ struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
+ struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
+ struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
+ struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
+#ifdef CONFIG_BIGNUM
+ struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */
+ struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */
+#endif
+ struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
+ struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
+ struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
+ struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */
+ struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */
+ struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
+ struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
+ struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
+ struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
+ struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
+ struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
+ struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
+ /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
+ struct JSFunctionBytecode *function_bytecode;
+ JSVarRef **var_refs;
+ JSObject *home_object; /* for 'super' access */
+ } func;
+ struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
+ JSContext *realm;
+ JSCFunctionType c_function;
+ uint8_t length;
+ uint8_t cproto;
+ int16_t magic;
+ } cfunc;
+ /* array part for fast arrays and typed arrays */
+ struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
+ union {
+ uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
+ struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
+ } u1;
+ union {
+ JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
+ void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
+ int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */
+ uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */
+ int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */
+ uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */
+ int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */
+ uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */
+ int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */
+ uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */
+ float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */
+ double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */
+ } u;
+ uint32_t count; /* <= 2^31-1. 0 for a detached typed array */
+ } array; /* 12/20 bytes */
+ JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */
+ JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */
+ } u;
+ /* byte sizes: 40/48/72 */
+};
+enum {
+ JS_ATOM_NULL_ = JS_ATOM_NULL,
+#define DEF(name, str) JS_ATOM_ ## name,
+#include "quickjs-atom.h"
+#undef DEF
+ JS_ATOM_END,
+};
+#define JS_ATOM_LAST_KEYWORD JS_ATOM_super
+#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
+
+static const char js_atom_init[] =
+#define DEF(name, str) str "\0"
+#include "quickjs-atom.h"
+#undef DEF
+;
+
+typedef enum OPCodeFormat {
+#define FMT(f) OP_FMT_ ## f,
+#define DEF(id, size, n_pop, n_push, f)
+#include "quickjs-opcode.h"
+#undef DEF
+#undef FMT
+} OPCodeFormat;
+
+enum OPCodeEnum {
+#define FMT(f)
+#define DEF(id, size, n_pop, n_push, f) OP_ ## id,
+#define def(id, size, n_pop, n_push, f)
+#include "quickjs-opcode.h"
+#undef def
+#undef DEF
+#undef FMT
+ OP_COUNT, /* excluding temporary opcodes */
+ /* temporary opcodes : overlap with the short opcodes */
+ OP_TEMP_START = OP_nop + 1,
+ OP___dummy = OP_TEMP_START - 1,
+#define FMT(f)
+#define DEF(id, size, n_pop, n_push, f)
+#define def(id, size, n_pop, n_push, f) OP_ ## id,
+#include "quickjs-opcode.h"
+#undef def
+#undef DEF
+#undef FMT
+ OP_TEMP_END,
+};
+
+static int JS_InitAtoms(JSRuntime *rt);
+static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len,
+ int atom_type);
+static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
+static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
+static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags);
+static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags);
+static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj, JSValueConst new_target,
+ int argc, JSValue *argv, int flags);
+static JSValue JS_CallConstructorInternal(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValue *argv, int flags);
+static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv);
+static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
+ int argc, JSValueConst *argv);
+static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor);
+static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+ JSValueConst val, int flags, int scope_idx);
+static maybe_unused void JS_DumpAtoms(JSRuntime *rt);
+static maybe_unused void JS_DumpString(JSRuntime *rt,
+ const JSString *p);
+static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
+static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
+static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
+static maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val);
+static maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
+static maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val);
+static maybe_unused void JS_DumpShapes(JSRuntime *rt);
+static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic);
+static void js_array_finalizer(JSRuntime *rt, JSValue val);
+static void js_array_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
+static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
+static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);
+static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
+static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
+static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
+static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);
+static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);
+static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_proxy_finalizer(JSRuntime *rt, JSValue val);
+static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_map_finalizer(JSRuntime *rt, JSValue val);
+static void js_map_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);
+static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);
+static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);
+static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
+static void js_generator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_promise_finalizer(JSRuntime *rt, JSValue val);
+static void js_promise_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
+static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+#ifdef CONFIG_BIGNUM
+static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
+static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+#endif
+static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
+static int JS_ToBoolFree(JSContext *ctx, JSValue val);
+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
+static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);
+static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);
+static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
+ JSValueConst flags);
+static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
+ JSValue pattern, JSValue bc);
+static void gc_decref(JSRuntime *rt);
+static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
+ const JSClassDef *class_def, JSAtom name);
+
+typedef enum JSStrictEqModeEnum {
+ JS_EQ_STRICT,
+ JS_EQ_SAME_VALUE,
+ JS_EQ_SAME_VALUE_ZERO,
+} JSStrictEqModeEnum;
+
+static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
+ JSStrictEqModeEnum eq_mode);
+static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2);
+static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);
+static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
+static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
+static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
+static JSProperty *add_property(JSContext *ctx,
+ JSObject *p, JSAtom prop, int prop_flags);
+#ifdef CONFIG_BIGNUM
+static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
+static JSValue JS_NewBigFloat(JSContext *ctx);
+static inline bf_t *JS_GetBigFloat(JSValueConst val)
+{
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ return &p->num;
+}
+static JSValue JS_NewBigDecimal(JSContext *ctx);
+static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)
+{
+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ return &p->num;
+}
+static JSValue JS_NewBigInt(JSContext *ctx);
+static inline bf_t *JS_GetBigInt(JSValueConst val)
+{
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ return &p->num;
+}
+static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
+ BOOL convert_to_safe_integer);
+static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
+static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
+static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);
+static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);
+static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
+ BOOL allow_null_or_undefined);
+static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
+#endif
+static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
+static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
+static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
+ JSValueConst proto_val, BOOL throw_flag);
+static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);
+static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);
+static int js_proxy_isArray(JSContext *ctx, JSValueConst obj);
+static int JS_CreateProperty(JSContext *ctx, JSObject *p,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags);
+static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);
+static void reset_weak_ref(JSRuntime *rt, JSObject *p);
+static JSValue js_array_buffer_constructor3(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len, JSClassID class_id,
+ uint8_t *buf,
+ JSFreeArrayBufferDataFunc *free_func,
+ void *opaque, BOOL alloc_flag);
+static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
+static JSValue js_typed_array_constructor(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int classid);
+static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
+static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
+static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
+static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
+ BOOL is_arg);
+static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int flags);
+static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);
+static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int flags, int scope_idx);
+static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
+static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
+ JS_MarkFunc *mark_func);
+static JSValue js_import_meta(JSContext *ctx);
+static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);
+static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
+static JSValue js_new_promise_capability(JSContext *ctx,
+ JSValue *resolving_funcs,
+ JSValueConst ctor);
+static warn_unused int perform_promise_then(JSContext *ctx,
+ JSValueConst promise,
+ JSValueConst *resolve_reject,
+ JSValueConst *cap_resolving_funcs);
+static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic);
+static int js_string_compare(JSContext *ctx,
+ const JSString *p1, const JSString *p2);
+static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
+static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSValue prop, JSValue val, int flags);
+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);
+static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);
+static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
+static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSObject *p, JSAtom prop);
+static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
+static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
+ JS_MarkFunc *mark_func);
+static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
+static void js_free_shape(JSRuntime *rt, JSShape *sh);
+static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
+static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
+ JSShapeProperty **pprs);
+static int init_shape_hash(JSRuntime *rt);
+static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres,
+ JSValueConst obj);
+static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres,
+ JSValueConst obj);
+static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
+static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
+ JSValueConst array_arg);
+static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
+ JSValue **arrpp, uint32_t *countp);
+static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
+ JSValueConst sync_iter);
+static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);
+static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int flags);
+static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
+static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
+ JSGCObjectTypeEnum type);
+static void remove_gc_object(JSGCObjectHeader *h);
+static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);
+static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
+static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
+ void *opaque);
+static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
+ JSAtom atom, void *opaque);
+void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);
+
+static const JSClassExoticMethods js_arguments_exotic_methods;
+static const JSClassExoticMethods js_string_exotic_methods;
+static const JSClassExoticMethods js_proxy_exotic_methods;
+static const JSClassExoticMethods js_module_ns_exotic_methods;
+static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
+
+static void js_trigger_gc(JSRuntime *rt, size_t size)
+{
+ BOOL force_gc;
+#ifdef FORCE_GC_AT_MALLOC
+ force_gc = TRUE;
+#else
+ force_gc = ((rt->malloc_state.malloc_size + size) >
+ rt->malloc_gc_threshold);
+#endif
+ if (force_gc) {
+#ifdef DUMP_GC
+ printf("GC: size=%" PRIu64 "\n",
+ (uint64_t)rt->malloc_state.malloc_size);
+#endif
+ JS_RunGC(rt);
+ rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
+ (rt->malloc_state.malloc_size >> 1);
+ }
+}
+
+static size_t js_malloc_usable_size_unknown(const void *ptr)
+{
+ return 0;
+}
+
+void *js_malloc_rt(JSRuntime *rt, size_t size)
+{
+ return rt->mf.js_malloc(&rt->malloc_state, size);
+}
+
+void js_free_rt(JSRuntime *rt, void *ptr)
+{
+ rt->mf.js_free(&rt->malloc_state, ptr);
+}
+
+void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
+{
+ return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
+}
+
+size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
+{
+ return rt->mf.js_malloc_usable_size(ptr);
+}
+
+void *js_mallocz_rt(JSRuntime *rt, size_t size)
+{
+ void *ptr;
+ ptr = js_malloc_rt(rt, size);
+ if (!ptr)
+ return NULL;
+ return memset(ptr, 0, size);
+}
+
+#ifdef CONFIG_BIGNUM
+/* called by libbf */
+static void *js_bf_realloc(void *opaque, void *ptr, size_t size)
+{
+ JSRuntime *rt = opaque;
+ return js_realloc_rt(rt, ptr, size);
+}
+#endif /* CONFIG_BIGNUM */
+
+/* Throw out of memory in case of error */
+void *js_malloc(JSContext *ctx, size_t size)
+{
+ void *ptr;
+ ptr = js_malloc_rt(ctx->rt, size);
+ if (unlikely(!ptr)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ return ptr;
+}
+
+/* Throw out of memory in case of error */
+void *js_mallocz(JSContext *ctx, size_t size)
+{
+ void *ptr;
+ ptr = js_mallocz_rt(ctx->rt, size);
+ if (unlikely(!ptr)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ return ptr;
+}
+
+void js_free(JSContext *ctx, void *ptr)
+{
+ js_free_rt(ctx->rt, ptr);
+}
+
+/* Throw out of memory in case of error */
+void *js_realloc(JSContext *ctx, void *ptr, size_t size)
+{
+ void *ret;
+ ret = js_realloc_rt(ctx->rt, ptr, size);
+ if (unlikely(!ret && size != 0)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ return ret;
+}
+
+/* store extra allocated size in *pslack if successful */
+void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)
+{
+ void *ret;
+ ret = js_realloc_rt(ctx->rt, ptr, size);
+ if (unlikely(!ret && size != 0)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ if (pslack) {
+ size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);
+ *pslack = (new_size > size) ? new_size - size : 0;
+ }
+ return ret;
+}
+
+size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)
+{
+ return js_malloc_usable_size_rt(ctx->rt, ptr);
+}
+
+/* Throw out of memory exception in case of error */
+char *js_strndup(JSContext *ctx, const char *s, size_t n)
+{
+ char *ptr;
+ ptr = js_malloc(ctx, n + 1);
+ if (ptr) {
+ memcpy(ptr, s, n);
+ ptr[n] = '\0';
+ }
+ return ptr;
+}
+
+char *js_strdup(JSContext *ctx, const char *str)
+{
+ return js_strndup(ctx, str, strlen(str));
+}
+
+static no_inline int js_realloc_array(JSContext *ctx, void **parray,
+ int elem_size, int *psize, int req_size)
+{
+ int new_size;
+ size_t slack;
+ void *new_array;
+ /* XXX: potential arithmetic overflow */
+ new_size = max_int(req_size, *psize * 3 / 2);
+ new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
+ if (!new_array)
+ return -1;
+ new_size += slack / elem_size;
+ *psize = new_size;
+ *parray = new_array;
+ return 0;
+}
+
+/* resize the array and update its size if req_size > *psize */
+static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
+ int *psize, int req_size)
+{
+ if (unlikely(req_size > *psize))
+ return js_realloc_array(ctx, parray, elem_size, psize, req_size);
+ else
+ return 0;
+}
+
+static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
+{
+ dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
+}
+
+static inline int is_digit(int c) {
+ return c >= '0' && c <= '9';
+}
+
+typedef struct JSClassShortDef {
+ JSAtom class_name;
+ JSClassFinalizer *finalizer;
+ JSClassGCMark *gc_mark;
+} JSClassShortDef;
+
+static JSClassShortDef const js_std_class_def[] = {
+ { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */
+ { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */
+ { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
+ { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
+ { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
+ { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
+ { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */
+ { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */
+ { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */
+ { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */
+ { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */
+ { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
+ { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
+ { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
+ { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
+ { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */
+ { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */
+ { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */
+ { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */
+ { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */
+ { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */
+ { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */
+ { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */
+ { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */
+ { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */
+ { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */
+ { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */
+#ifdef CONFIG_BIGNUM
+ { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */
+ { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */
+#endif
+ { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */
+ { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */
+ { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */
+#ifdef CONFIG_BIGNUM
+ { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */
+ { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */
+ { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */
+ { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */
+ { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */
+#endif
+ { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */
+ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */
+ { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */
+ { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */
+ { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */
+ { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
+ { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
+ { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
+ { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
+ { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
+};
+
+static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
+ int start, int count)
+{
+ JSClassDef cm_s, *cm = &cm_s;
+ int i, class_id;
+
+ for(i = 0; i < count; i++) {
+ class_id = i + start;
+ memset(cm, 0, sizeof(*cm));
+ cm->finalizer = tab[i].finalizer;
+ cm->gc_mark = tab[i].gc_mark;
+ if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_BIGNUM
+static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "unsupported operation");
+}
+
+static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)
+{
+ return JS_ThrowUnsupportedOperation(ctx);
+}
+
+static JSValue invalid_from_string(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent)
+{
+ return JS_NAN;
+}
+
+static int invalid_unary_arith(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1)
+{
+ JS_FreeValue(ctx, op1);
+ JS_ThrowUnsupportedOperation(ctx);
+ return -1;
+}
+
+static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2)
+{
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ JS_ThrowUnsupportedOperation(ctx);
+ return -1;
+}
+
+static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
+ int64_t exponent)
+{
+ return JS_ThrowUnsupportedOperation(ctx);
+}
+
+static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)
+{
+ JS_ThrowUnsupportedOperation(ctx);
+ return -1;
+}
+
+static void set_dummy_numeric_ops(JSNumericOperations *ops)
+{
+ ops->to_string = invalid_to_string;
+ ops->from_string = invalid_from_string;
+ ops->unary_arith = invalid_unary_arith;
+ ops->binary_arith = invalid_binary_arith;
+ ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;
+ ops->mul_pow10 = invalid_mul_pow10;
+}
+
+#endif /* CONFIG_BIGNUM */
+
+#if !defined(CONFIG_STACK_CHECK)
+/* no stack limitation */
+static inline uintptr_t js_get_stack_pointer(void)
+{
+ return 0;
+}
+
+static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
+{
+ return FALSE;
+}
+#else
+// Uses code from LLVM project.
+static inline uintptr_t js_get_stack_pointer(void)
+{
+#ifdef _MSC_VER
+ return (uintptr_t) _AddressOfReturnAddress();
+#elif defined __has_builtin
+#if __has_builtin(__builtin_frame_address)
+ return (uintptr_t) __builtin_frame_address(0);
+#endif
+#elif defined __GNUC__
+ return (uintptr_t) __builtin_frame_address(0);
+#else
+ char CharOnStack = 0;
+ // The volatile store here is intended to escape the local variable, to
+ // prevent the compiler from optimizing CharOnStack into anything other
+ // than a char on the stack.
+ //
+ // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19.
+ char *volatile Ptr = &CharOnStack;
+ return (uintptr_t) Ptr;
+#endif
+}
+
+static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
+{
+ uintptr_t sp;
+ sp = js_get_stack_pointer() - alloca_size;
+ return unlikely(sp < rt->stack_limit);
+}
+#endif
+
+JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
+{
+ JSRuntime *rt;
+ JSMallocState ms;
+
+ memset(&ms, 0, sizeof(ms));
+ ms.opaque = opaque;
+ ms.malloc_limit = -1;
+
+ rt = mf->js_malloc(&ms, sizeof(JSRuntime));
+ if (!rt)
+ return NULL;
+ memset(rt, 0, sizeof(*rt));
+ rt->mf = *mf;
+ if (!rt->mf.js_malloc_usable_size) {
+ /* use dummy function if none provided */
+ rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
+ }
+ rt->malloc_state = ms;
+ rt->malloc_gc_threshold = 256 * 1024;
+
+#ifdef CONFIG_BIGNUM
+ bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
+ set_dummy_numeric_ops(&rt->bigint_ops);
+ set_dummy_numeric_ops(&rt->bigfloat_ops);
+ set_dummy_numeric_ops(&rt->bigdecimal_ops);
+#endif
+
+ init_list_head(&rt->context_list);
+ init_list_head(&rt->gc_obj_list);
+ init_list_head(&rt->gc_zero_ref_count_list);
+ rt->gc_phase = JS_GC_PHASE_NONE;
+
+#ifdef DUMP_LEAKS
+ init_list_head(&rt->string_list);
+#endif
+ init_list_head(&rt->job_list);
+
+ if (JS_InitAtoms(rt))
+ goto fail;
+
+ /* create the object, array and function classes */
+ if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
+ countof(js_std_class_def)) < 0)
+ goto fail;
+ rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
+ rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
+ rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
+
+ rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
+ rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
+ rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
+ rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
+ if (init_shape_hash(rt))
+ goto fail;
+
+ rt->stack_size = JS_DEFAULT_STACK_SIZE;
+ JS_UpdateStackTop(rt);
+
+ rt->current_exception = JS_NULL;
+
+ return rt;
+ fail:
+ JS_FreeRuntime(rt);
+ return NULL;
+}
+
+void *JS_GetRuntimeOpaque(JSRuntime *rt)
+{
+ return rt->user_opaque;
+}
+
+void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
+{
+ rt->user_opaque = opaque;
+}
+
+/* default memory allocation functions with memory limitation */
+static inline size_t js_def_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 *js_def_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);
+ if (!ptr)
+ return NULL;
+
+ s->malloc_count++;
+ s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
+ return ptr;
+}
+
+static void js_def_free(JSMallocState *s, void *ptr)
+{
+ if (!ptr)
+ return;
+
+ s->malloc_count--;
+ s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
+ free(ptr);
+}
+
+static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
+{
+ size_t old_size;
+
+ if (!ptr) {
+ if (size == 0)
+ return NULL;
+ return js_def_malloc(s, size);
+ }
+ old_size = js_def_malloc_usable_size(ptr);
+ if (size == 0) {
+ 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;
+
+ ptr = realloc(ptr, size);
+ if (!ptr)
+ return NULL;
+
+ s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;
+ return ptr;
+}
+
+static const JSMallocFunctions def_malloc_funcs = {
+ js_def_malloc,
+ js_def_free,
+ js_def_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
+};
+
+JSRuntime *JS_NewRuntime(void)
+{
+ return JS_NewRuntime2(&def_malloc_funcs, NULL);
+}
+
+void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
+{
+ rt->malloc_state.malloc_limit = limit;
+}
+
+/* use -1 to disable automatic GC */
+void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
+{
+ rt->malloc_gc_threshold = gc_threshold;
+}
+
+#define malloc(s) malloc_is_forbidden(s)
+#define free(p) free_is_forbidden(p)
+#define realloc(p,s) realloc_is_forbidden(p,s)
+
+void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
+{
+ rt->interrupt_handler = cb;
+ rt->interrupt_opaque = opaque;
+}
+
+void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
+{
+ rt->can_block = can_block;
+}
+
+void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
+ const JSSharedArrayBufferFunctions *sf)
+{
+ rt->sab_funcs = *sf;
+}
+
+/* return 0 if OK, < 0 if exception */
+int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
+ int argc, JSValueConst *argv)
+{
+ JSRuntime *rt = ctx->rt;
+ JSJobEntry *e;
+ int i;
+
+ e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
+ if (!e)
+ return -1;
+ e->ctx = ctx;
+ e->job_func = job_func;
+ e->argc = argc;
+ for(i = 0; i < argc; i++) {
+ e->argv[i] = JS_DupValue(ctx, argv[i]);
+ }
+ list_add_tail(&e->link, &rt->job_list);
+ return 0;
+}
+
+BOOL JS_IsJobPending(JSRuntime *rt)
+{
+ return !list_empty(&rt->job_list);
+}
+
+/* return < 0 if exception, 0 if no job pending, 1 if a job was
+ executed successfully. the context of the job is stored in '*pctx' */
+int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
+{
+ JSContext *ctx;
+ JSJobEntry *e;
+ JSValue res;
+ int i, ret;
+
+ if (list_empty(&rt->job_list)) {
+ *pctx = NULL;
+ return 0;
+ }
+
+ /* get the first pending job and execute it */
+ e = list_entry(rt->job_list.next, JSJobEntry, link);
+ list_del(&e->link);
+ ctx = e->ctx;
+ res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);
+ for(i = 0; i < e->argc; i++)
+ JS_FreeValue(ctx, e->argv[i]);
+ if (JS_IsException(res))
+ ret = -1;
+ else
+ ret = 1;
+ JS_FreeValue(ctx, res);
+ js_free(ctx, e);
+ *pctx = ctx;
+ return ret;
+}
+
+static inline uint32_t atom_get_free(const JSAtomStruct *p)
+{
+ return (uintptr_t)p >> 1;
+}
+
+static inline BOOL atom_is_free(const JSAtomStruct *p)
+{
+ return (uintptr_t)p & 1;
+}
+
+static inline JSAtomStruct *atom_set_free(uint32_t v)
+{
+ return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);
+}
+
+/* Note: the string contents are uninitialized */
+static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
+{
+ JSString *str;
+ str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
+ if (unlikely(!str))
+ return NULL;
+ str->header.ref_count = 1;
+ str->is_wide_char = is_wide_char;
+ str->len = max_len;
+ str->atom_type = 0;
+ str->hash = 0; /* optional but costless */
+ str->hash_next = 0; /* optional */
+#ifdef DUMP_LEAKS
+ list_add_tail(&str->link, &rt->string_list);
+#endif
+ return str;
+}
+
+static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
+{
+ JSString *p;
+ p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
+ if (unlikely(!p)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ return p;
+}
+
+/* same as JS_FreeValueRT() but faster */
+static inline void js_free_string(JSRuntime *rt, JSString *str)
+{
+ if (--str->header.ref_count <= 0) {
+ if (str->atom_type) {
+ JS_FreeAtomStruct(rt, str);
+ } else {
+#ifdef DUMP_LEAKS
+ list_del(&str->link);
+#endif
+ js_free_rt(rt, str);
+ }
+ }
+}
+
+void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
+{
+ if (rt)
+ rt->rt_info = s;
+}
+
+void JS_FreeRuntime(JSRuntime *rt)
+{
+ struct list_head *el, *el1;
+ int i;
+
+ JS_FreeValueRT(rt, rt->current_exception);
+
+ list_for_each_safe(el, el1, &rt->job_list) {
+ JSJobEntry *e = list_entry(el, JSJobEntry, link);
+ for(i = 0; i < e->argc; i++)
+ JS_FreeValueRT(rt, e->argv[i]);
+ js_free_rt(rt, e);
+ }
+ init_list_head(&rt->job_list);
+
+ JS_RunGC(rt);
+
+#ifdef DUMP_LEAKS
+ /* leaking objects */
+ {
+ BOOL header_done;
+ JSGCObjectHeader *p;
+ int count;
+
+ /* remove the internal refcounts to display only the object
+ referenced externally */
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ p->mark = 0;
+ }
+ gc_decref(rt);
+
+ header_done = FALSE;
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ if (p->ref_count != 0) {
+ if (!header_done) {
+ printf("Object leaks:\n");
+ JS_DumpObjectHeader(rt);
+ header_done = TRUE;
+ }
+ JS_DumpGCObject(rt, p);
+ }
+ }
+
+ count = 0;
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ if (p->ref_count == 0) {
+ count++;
+ }
+ }
+ if (count != 0)
+ printf("Secondary object leaks: %d\n", count);
+ }
+#endif
+ fflush(stdout);
+ assert(list_empty(&rt->gc_obj_list));
+
+ /* free the classes */
+ for(i = 0; i < rt->class_count; i++) {
+ JSClass *cl = &rt->class_array[i];
+ if (cl->class_id != 0) {
+ JS_FreeAtomRT(rt, cl->class_name);
+ }
+ }
+ js_free_rt(rt, rt->class_array);
+
+#ifdef CONFIG_BIGNUM
+ bf_context_end(&rt->bf_ctx);
+#endif
+
+#ifdef DUMP_LEAKS
+ /* only the atoms defined in JS_InitAtoms() should be left */
+ {
+ BOOL header_done = FALSE;
+
+ for(i = 0; i < rt->atom_size; i++) {
+ JSAtomStruct *p = rt->atom_array[i];
+ if (!atom_is_free(p) /* && p->str*/) {
+ if (i >= JS_ATOM_END || p->header.ref_count != 1) {
+ if (!header_done) {
+ header_done = TRUE;
+ if (rt->rt_info) {
+ printf("%s:1: atom leakage:", rt->rt_info);
+ } else {
+ printf("Atom leaks:\n"
+ " %6s %6s %s\n",
+ "ID", "REFCNT", "NAME");
+ }
+ }
+ if (rt->rt_info) {
+ printf(" ");
+ } else {
+ printf(" %6u %6u ", i, p->header.ref_count);
+ }
+ switch (p->atom_type) {
+ case JS_ATOM_TYPE_STRING:
+ JS_DumpString(rt, p);
+ break;
+ case JS_ATOM_TYPE_GLOBAL_SYMBOL:
+ printf("Symbol.for(");
+ JS_DumpString(rt, p);
+ printf(")");
+ break;
+ case JS_ATOM_TYPE_SYMBOL:
+ if (p->hash == JS_ATOM_HASH_SYMBOL) {
+ printf("Symbol(");
+ JS_DumpString(rt, p);
+ printf(")");
+ } else {
+ printf("Private(");
+ JS_DumpString(rt, p);
+ printf(")");
+ }
+ break;
+ }
+ if (rt->rt_info) {
+ printf(":%u", p->header.ref_count);
+ } else {
+ printf("\n");
+ }
+ }
+ }
+ }
+ if (rt->rt_info && header_done)
+ printf("\n");
+ }
+#endif
+
+ /* free the atoms */
+ for(i = 0; i < rt->atom_size; i++) {
+ JSAtomStruct *p = rt->atom_array[i];
+ if (!atom_is_free(p)) {
+#ifdef DUMP_LEAKS
+ list_del(&p->link);
+#endif
+ js_free_rt(rt, p);
+ }
+ }
+ js_free_rt(rt, rt->atom_array);
+ js_free_rt(rt, rt->atom_hash);
+ js_free_rt(rt, rt->shape_hash);
+#ifdef DUMP_LEAKS
+ if (!list_empty(&rt->string_list)) {
+ if (rt->rt_info) {
+ printf("%s:1: string leakage:", rt->rt_info);
+ } else {
+ printf("String leaks:\n"
+ " %6s %s\n",
+ "REFCNT", "VALUE");
+ }
+ list_for_each_safe(el, el1, &rt->string_list) {
+ JSString *str = list_entry(el, JSString, link);
+ if (rt->rt_info) {
+ printf(" ");
+ } else {
+ printf(" %6u ", str->header.ref_count);
+ }
+ JS_DumpString(rt, str);
+ if (rt->rt_info) {
+ printf(":%u", str->header.ref_count);
+ } else {
+ printf("\n");
+ }
+ list_del(&str->link);
+ js_free_rt(rt, str);
+ }
+ if (rt->rt_info)
+ printf("\n");
+ }
+ {
+ JSMallocState *s = &rt->malloc_state;
+ if (s->malloc_count > 1) {
+ if (rt->rt_info)
+ printf("%s:1: ", rt->rt_info);
+ printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",
+ (uint64_t)(s->malloc_size - sizeof(JSRuntime)),
+ (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);
+ }
+ }
+#endif
+
+ {
+ JSMallocState ms = rt->malloc_state;
+ rt->mf.js_free(&ms, rt);
+ }
+}
+
+JSContext *JS_NewContextRaw(JSRuntime *rt)
+{
+ JSContext *ctx;
+ int i;
+
+ ctx = js_mallocz_rt(rt, sizeof(JSContext));
+ if (!ctx)
+ return NULL;
+ ctx->header.ref_count = 1;
+ add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
+
+ ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *
+ rt->class_count);
+ if (!ctx->class_proto) {
+ js_free_rt(rt, ctx);
+ return NULL;
+ }
+ ctx->rt = rt;
+ list_add_tail(&ctx->link, &rt->context_list);
+#ifdef CONFIG_BIGNUM
+ ctx->bf_ctx = &rt->bf_ctx;
+ ctx->fp_env.prec = 113;
+ ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;
+#endif
+ for(i = 0; i < rt->class_count; i++)
+ ctx->class_proto[i] = JS_NULL;
+ ctx->array_ctor = JS_NULL;
+ ctx->regexp_ctor = JS_NULL;
+ ctx->promise_ctor = JS_NULL;
+ init_list_head(&ctx->loaded_modules);
+
+ JS_AddIntrinsicBasicObjects(ctx);
+ return ctx;
+}
+
+JSContext *JS_NewContext(JSRuntime *rt)
+{
+ JSContext *ctx;
+
+ ctx = JS_NewContextRaw(rt);
+ if (!ctx)
+ return NULL;
+
+ JS_AddIntrinsicBaseObjects(ctx);
+ JS_AddIntrinsicDate(ctx);
+ JS_AddIntrinsicEval(ctx);
+ JS_AddIntrinsicStringNormalize(ctx);
+ JS_AddIntrinsicRegExp(ctx);
+ JS_AddIntrinsicJSON(ctx);
+ JS_AddIntrinsicProxy(ctx);
+ JS_AddIntrinsicMapSet(ctx);
+ JS_AddIntrinsicTypedArrays(ctx);
+ JS_AddIntrinsicPromise(ctx);
+#ifdef CONFIG_BIGNUM
+ JS_AddIntrinsicBigInt(ctx);
+#endif
+ return ctx;
+}
+
+void *JS_GetContextOpaque(JSContext *ctx)
+{
+ return ctx->user_opaque;
+}
+
+void JS_SetContextOpaque(JSContext *ctx, void *opaque)
+{
+ ctx->user_opaque = opaque;
+}
+
+/* set the new value and free the old value after (freeing the value
+ can reallocate the object data) */
+static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)
+{
+ JSValue old_val;
+ old_val = *pval;
+ *pval = new_val;
+ JS_FreeValue(ctx, old_val);
+}
+
+void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)
+{
+ JSRuntime *rt = ctx->rt;
+ assert(class_id < rt->class_count);
+ set_value(ctx, &ctx->class_proto[class_id], obj);
+}
+
+JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
+{
+ JSRuntime *rt = ctx->rt;
+ assert(class_id < rt->class_count);
+ return JS_DupValue(ctx, ctx->class_proto[class_id]);
+}
+
+typedef enum JSFreeModuleEnum {
+ JS_FREE_MODULE_ALL,
+ JS_FREE_MODULE_NOT_RESOLVED,
+ JS_FREE_MODULE_NOT_EVALUATED,
+} JSFreeModuleEnum;
+
+/* XXX: would be more efficient with separate module lists */
+static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
+{
+ struct list_head *el, *el1;
+ list_for_each_safe(el, el1, &ctx->loaded_modules) {
+ JSModuleDef *m = list_entry(el, JSModuleDef, link);
+ if (flag == JS_FREE_MODULE_ALL ||
+ (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||
+ (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {
+ js_free_module_def(ctx, m);
+ }
+ }
+}
+
+JSContext *JS_DupContext(JSContext *ctx)
+{
+ ctx->header.ref_count++;
+ return ctx;
+}
+
+/* used by the GC */
+static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
+ JS_MarkFunc *mark_func)
+{
+ int i;
+ struct list_head *el;
+
+ /* modules are not seen by the GC, so we directly mark the objects
+ referenced by each module */
+ list_for_each(el, &ctx->loaded_modules) {
+ JSModuleDef *m = list_entry(el, JSModuleDef, link);
+ js_mark_module_def(rt, m, mark_func);
+ }
+
+ JS_MarkValue(rt, ctx->global_obj, mark_func);
+ JS_MarkValue(rt, ctx->global_var_obj, mark_func);
+
+ JS_MarkValue(rt, ctx->throw_type_error, mark_func);
+ JS_MarkValue(rt, ctx->eval_obj, mark_func);
+
+ JS_MarkValue(rt, ctx->array_proto_values, mark_func);
+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
+ JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
+ }
+ for(i = 0; i < rt->class_count; i++) {
+ JS_MarkValue(rt, ctx->class_proto[i], mark_func);
+ }
+ JS_MarkValue(rt, ctx->iterator_proto, mark_func);
+ JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
+ JS_MarkValue(rt, ctx->promise_ctor, mark_func);
+ JS_MarkValue(rt, ctx->array_ctor, mark_func);
+ JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
+ JS_MarkValue(rt, ctx->function_ctor, mark_func);
+ JS_MarkValue(rt, ctx->function_proto, mark_func);
+
+ if (ctx->array_shape)
+ mark_func(rt, &ctx->array_shape->header);
+}
+
+void JS_FreeContext(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ int i;
+
+ if (--ctx->header.ref_count > 0)
+ return;
+ assert(ctx->header.ref_count == 0);
+
+#ifdef DUMP_ATOMS
+ JS_DumpAtoms(ctx->rt);
+#endif
+#ifdef DUMP_SHAPES
+ JS_DumpShapes(ctx->rt);
+#endif
+#ifdef DUMP_OBJECTS
+ {
+ struct list_head *el;
+ JSGCObjectHeader *p;
+ printf("JSObjects: {\n");
+ JS_DumpObjectHeader(ctx->rt);
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ JS_DumpGCObject(rt, p);
+ }
+ printf("}\n");
+ }
+#endif
+#ifdef DUMP_MEM
+ {
+ JSMemoryUsage stats;
+ JS_ComputeMemoryUsage(rt, &stats);
+ JS_DumpMemoryUsage(stdout, &stats, rt);
+ }
+#endif
+
+ js_free_modules(ctx, JS_FREE_MODULE_ALL);
+
+ JS_FreeValue(ctx, ctx->global_obj);
+ JS_FreeValue(ctx, ctx->global_var_obj);
+
+ JS_FreeValue(ctx, ctx->throw_type_error);
+ JS_FreeValue(ctx, ctx->eval_obj);
+
+ JS_FreeValue(ctx, ctx->array_proto_values);
+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
+ JS_FreeValue(ctx, ctx->native_error_proto[i]);
+ }
+ for(i = 0; i < rt->class_count; i++) {
+ JS_FreeValue(ctx, ctx->class_proto[i]);
+ }
+ js_free_rt(rt, ctx->class_proto);
+ JS_FreeValue(ctx, ctx->iterator_proto);
+ JS_FreeValue(ctx, ctx->async_iterator_proto);
+ JS_FreeValue(ctx, ctx->promise_ctor);
+ JS_FreeValue(ctx, ctx->array_ctor);
+ JS_FreeValue(ctx, ctx->regexp_ctor);
+ JS_FreeValue(ctx, ctx->function_ctor);
+ JS_FreeValue(ctx, ctx->function_proto);
+
+ js_free_shape_null(ctx->rt, ctx->array_shape);
+
+ list_del(&ctx->link);
+ remove_gc_object(&ctx->header);
+ js_free_rt(ctx->rt, ctx);
+}
+
+JSRuntime *JS_GetRuntime(JSContext *ctx)
+{
+ return ctx->rt;
+}
+
+static void update_stack_limit(JSRuntime *rt)
+{
+ if (rt->stack_size == 0) {
+ rt->stack_limit = 0; /* no limit */
+ } else {
+ rt->stack_limit = rt->stack_top - rt->stack_size;
+ }
+}
+
+void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)
+{
+ rt->stack_size = stack_size;
+ update_stack_limit(rt);
+}
+
+void JS_UpdateStackTop(JSRuntime *rt)
+{
+ rt->stack_top = js_get_stack_pointer();
+ update_stack_limit(rt);
+}
+
+static inline BOOL is_strict_mode(JSContext *ctx)
+{
+ JSStackFrame *sf = ctx->rt->current_stack_frame;
+ return (sf && (sf->js_mode & JS_MODE_STRICT));
+}
+
+#ifdef CONFIG_BIGNUM
+static inline BOOL is_math_mode(JSContext *ctx)
+{
+ JSStackFrame *sf = ctx->rt->current_stack_frame;
+ return (sf && (sf->js_mode & JS_MODE_MATH));
+}
+#endif
+
+/* JSAtom support */
+
+#define JS_ATOM_TAG_INT (1U << 31)
+#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
+#define JS_ATOM_MAX ((1U << 30) - 1)
+
+/* return the max count from the hash size */
+#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
+
+static inline BOOL JS_AtomIsConst(JSAtom v)
+{
+#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
+ return (int32_t)v <= 0;
+#else
+ return (int32_t)v < JS_ATOM_END;
+#endif
+}
+
+static inline BOOL JS_AtomIsTaggedInt(JSAtom v)
+{
+ return (v & JS_ATOM_TAG_INT) != 0;
+}
+
+static inline JSAtom JS_AtomFromUInt32(uint32_t v)
+{
+ return v | JS_ATOM_TAG_INT;
+}
+
+static inline uint32_t JS_AtomToUInt32(JSAtom atom)
+{
+ return atom & ~JS_ATOM_TAG_INT;
+}
+
+static inline int is_num(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
+static inline BOOL is_num_string(uint32_t *pval, const JSString *p)
+{
+ uint32_t n;
+ uint64_t n64;
+ int c, i, len;
+
+ len = p->len;
+ if (len == 0 || len > 10)
+ return FALSE;
+ if (p->is_wide_char)
+ c = p->u.str16[0];
+ else
+ c = p->u.str8[0];
+ if (is_num(c)) {
+ if (c == '0') {
+ if (len != 1)
+ return FALSE;
+ n = 0;
+ } else {
+ n = c - '0';
+ for(i = 1; i < len; i++) {
+ if (p->is_wide_char)
+ c = p->u.str16[i];
+ else
+ c = p->u.str8[i];
+ if (!is_num(c))
+ return FALSE;
+ n64 = (uint64_t)n * 10 + (c - '0');
+ if ((n64 >> 32) != 0)
+ return FALSE;
+ n = n64;
+ }
+ }
+ *pval = n;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/* XXX: could use faster version ? */
+static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)
+{
+ size_t i;
+
+ for(i = 0; i < len; i++)
+ h = h * 263 + str[i];
+ return h;
+}
+
+static inline uint32_t hash_string16(const uint16_t *str,
+ size_t len, uint32_t h)
+{
+ size_t i;
+
+ for(i = 0; i < len; i++)
+ h = h * 263 + str[i];
+ return h;
+}
+
+static uint32_t hash_string(const JSString *str, uint32_t h)
+{
+ if (str->is_wide_char)
+ h = hash_string16(str->u.str16, str->len, h);
+ else
+ h = hash_string8(str->u.str8, str->len, h);
+ return h;
+}
+
+static maybe_unused void JS_DumpString(JSRuntime *rt,
+ const JSString *p)
+{
+ int i, c, sep;
+
+ if (p == NULL) {
+ printf("<null>");
+ return;
+ }
+ printf("%d", p->header.ref_count);
+ sep = (p->header.ref_count == 1) ? '\"' : '\'';
+ putchar(sep);
+ for(i = 0; i < p->len; i++) {
+ if (p->is_wide_char)
+ c = p->u.str16[i];
+ else
+ c = p->u.str8[i];
+ if (c == sep || c == '\\') {
+ putchar('\\');
+ putchar(c);
+ } else if (c >= ' ' && c <= 126) {
+ putchar(c);
+ } else if (c == '\n') {
+ putchar('\\');
+ putchar('n');
+ } else {
+ printf("\\u%04x", c);
+ }
+ }
+ putchar(sep);
+}
+
+static maybe_unused void JS_DumpAtoms(JSRuntime *rt)
+{
+ JSAtomStruct *p;
+ int h, i;
+ /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */
+ printf("JSAtom count=%d size=%d hash_size=%d:\n",
+ rt->atom_count, rt->atom_size, rt->atom_hash_size);
+ printf("JSAtom hash table: {\n");
+ for(i = 0; i < rt->atom_hash_size; i++) {
+ h = rt->atom_hash[i];
+ if (h) {
+ printf(" %d:", i);
+ while (h) {
+ p = rt->atom_array[h];
+ printf(" ");
+ JS_DumpString(rt, p);
+ h = p->hash_next;
+ }
+ printf("\n");
+ }
+ }
+ printf("}\n");
+ printf("JSAtom table: {\n");
+ for(i = 0; i < rt->atom_size; i++) {
+ p = rt->atom_array[i];
+ if (!atom_is_free(p)) {
+ printf(" %d: { %d %08x ", i, p->atom_type, p->hash);
+ if (!(p->len == 0 && p->is_wide_char != 0))
+ JS_DumpString(rt, p);
+ printf(" %d }\n", p->hash_next);
+ }
+ }
+ printf("}\n");
+}
+
+static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)
+{
+ JSAtomStruct *p;
+ uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;
+
+ assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */
+ new_hash_mask = new_hash_size - 1;
+ new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);
+ if (!new_hash)
+ return -1;
+ for(i = 0; i < rt->atom_hash_size; i++) {
+ h = rt->atom_hash[i];
+ while (h != 0) {
+ p = rt->atom_array[h];
+ hash_next1 = p->hash_next;
+ /* add in new hash table */
+ j = p->hash & new_hash_mask;
+ p->hash_next = new_hash[j];
+ new_hash[j] = h;
+ h = hash_next1;
+ }
+ }
+ js_free_rt(rt, rt->atom_hash);
+ rt->atom_hash = new_hash;
+ rt->atom_hash_size = new_hash_size;
+ rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);
+ // JS_DumpAtoms(rt);
+ return 0;
+}
+
+static int JS_InitAtoms(JSRuntime *rt)
+{
+ int i, len, atom_type;
+ const char *p;
+
+ rt->atom_hash_size = 0;
+ rt->atom_hash = NULL;
+ rt->atom_count = 0;
+ rt->atom_size = 0;
+ rt->atom_free_index = 0;
+ if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */
+ return -1;
+
+ p = js_atom_init;
+ for(i = 1; i < JS_ATOM_END; i++) {
+ if (i == JS_ATOM_Private_brand)
+ atom_type = JS_ATOM_TYPE_PRIVATE;
+ else if (i >= JS_ATOM_Symbol_toPrimitive)
+ atom_type = JS_ATOM_TYPE_SYMBOL;
+ else
+ atom_type = JS_ATOM_TYPE_STRING;
+ len = strlen(p);
+ if (JS_NewAtomInitImpl(rt, p, len, atom_type) == JS_ATOM_NULL)
+ return -1;
+ p = p + len + 1;
+ }
+ return 0;
+}
+
+static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
+{
+ JSAtomStruct *p;
+
+ if (!JS_AtomIsConst(v)) {
+ p = rt->atom_array[v];
+ p->header.ref_count++;
+ }
+ return v;
+}
+
+JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
+{
+ JSRuntime *rt;
+ JSAtomStruct *p;
+
+ if (!JS_AtomIsConst(v)) {
+ rt = ctx->rt;
+ p = rt->atom_array[v];
+ p->header.ref_count++;
+ }
+ return v;
+}
+
+static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
+{
+ JSRuntime *rt;
+ JSAtomStruct *p;
+
+ rt = ctx->rt;
+ if (JS_AtomIsTaggedInt(v))
+ return JS_ATOM_KIND_STRING;
+ p = rt->atom_array[v];
+ switch(p->atom_type) {
+ case JS_ATOM_TYPE_STRING:
+ return JS_ATOM_KIND_STRING;
+ case JS_ATOM_TYPE_GLOBAL_SYMBOL:
+ return JS_ATOM_KIND_SYMBOL;
+ case JS_ATOM_TYPE_SYMBOL:
+ switch(p->hash) {
+ case JS_ATOM_HASH_SYMBOL:
+ return JS_ATOM_KIND_SYMBOL;
+ case JS_ATOM_HASH_PRIVATE:
+ return JS_ATOM_KIND_PRIVATE;
+ default:
+ abort();
+ }
+ default:
+ abort();
+ }
+}
+
+static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
+{
+ return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
+}
+
+static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
+{
+ uint32_t i = p->hash_next; /* atom_index */
+ if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
+ JSAtomStruct *p1;
+
+ i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
+ p1 = rt->atom_array[i];
+ while (p1 != p) {
+ assert(i != 0);
+ i = p1->hash_next;
+ p1 = rt->atom_array[i];
+ }
+ }
+ return i;
+}
+
+/* string case (internal). Return JS_ATOM_NULL if error. 'str' is
+ freed. */
+static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type)
+{
+ uint32_t h, h1, i;
+ JSAtomStruct *p;
+ int len;
+
+#if 0
+ printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n");
+#endif
+ if (atom_type < JS_ATOM_TYPE_SYMBOL) {
+ /* str is not NULL */
+ if (str->atom_type == atom_type) {
+ /* str is the atom, return its index */
+ i = js_get_atom_index(rt, str);
+ /* reduce string refcount and increase atom's unless constant */
+ if (JS_AtomIsConst(i))
+ str->header.ref_count--;
+ return i;
+ }
+ /* try and locate an already registered atom */
+ len = str->len;
+ h = hash_string(str, atom_type);
+ h &= JS_ATOM_HASH_MASK;
+ h1 = h & (rt->atom_hash_size - 1);
+ i = rt->atom_hash[h1];
+ while (i != 0) {
+ p = rt->atom_array[i];
+ if (p->hash == h &&
+ p->atom_type == atom_type &&
+ p->len == len &&
+ js_string_memcmp(p, str, len) == 0) {
+ if (!JS_AtomIsConst(i))
+ p->header.ref_count++;
+ goto done;
+ }
+ i = p->hash_next;
+ }
+ } else {
+ h1 = 0; /* avoid warning */
+ if (atom_type == JS_ATOM_TYPE_SYMBOL) {
+ h = JS_ATOM_HASH_SYMBOL;
+ } else {
+ h = JS_ATOM_HASH_PRIVATE;
+ atom_type = JS_ATOM_TYPE_SYMBOL;
+ }
+ }
+
+ if (rt->atom_free_index == 0) {
+ /* allow new atom entries */
+ uint32_t new_size, start;
+ JSAtomStruct **new_array;
+
+ /* alloc new with size progression 3/2:
+ 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
+ preallocating space for predefined atoms (at least 195).
+ */
+ new_size = max_int(211, rt->atom_size * 3 / 2);
+ if (new_size > JS_ATOM_MAX)
+ goto fail;
+ /* XXX: should use realloc2 to use slack space */
+ new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
+ if (!new_array)
+ goto fail;
+ /* Note: the atom 0 is not used */
+ start = rt->atom_size;
+ if (start == 0) {
+ /* JS_ATOM_NULL entry */
+ p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
+ if (!p) {
+ js_free_rt(rt, new_array);
+ goto fail;
+ }
+ p->header.ref_count = 1; /* not refcounted */
+ p->atom_type = JS_ATOM_TYPE_SYMBOL;
+#ifdef DUMP_LEAKS
+ list_add_tail(&p->link, &rt->string_list);
+#endif
+ new_array[0] = p;
+ rt->atom_count++;
+ start = 1;
+ }
+ rt->atom_size = new_size;
+ rt->atom_array = new_array;
+ rt->atom_free_index = start;
+ for(i = start; i < new_size; i++) {
+ uint32_t next;
+ if (i == (new_size - 1))
+ next = 0;
+ else
+ next = i + 1;
+ rt->atom_array[i] = atom_set_free(next);
+ }
+ }
+
+ if (str) {
+ if (str->atom_type == 0) {
+ p = str;
+ p->atom_type = atom_type;
+ } else {
+ p = js_malloc_rt(rt, sizeof(JSString) +
+ (str->len << str->is_wide_char) +
+ 1 - str->is_wide_char);
+ if (unlikely(!p))
+ goto fail;
+ p->header.ref_count = 1;
+ p->is_wide_char = str->is_wide_char;
+ p->len = str->len;
+#ifdef DUMP_LEAKS
+ list_add_tail(&p->link, &rt->string_list);
+#endif
+ memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
+ 1 - str->is_wide_char);
+ js_free_string(rt, str);
+ }
+ } else {
+ p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
+ if (!p)
+ return JS_ATOM_NULL;
+ p->header.ref_count = 1;
+ p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
+ p->len = 0;
+#ifdef DUMP_LEAKS
+ list_add_tail(&p->link, &rt->string_list);
+#endif
+ }
+
+ /* use an already free entry */
+ i = rt->atom_free_index;
+ rt->atom_free_index = atom_get_free(rt->atom_array[i]);
+ rt->atom_array[i] = p;
+
+ p->hash = h;
+ p->hash_next = i; /* atom_index */
+ p->atom_type = atom_type;
+
+ rt->atom_count++;
+
+ if (atom_type != JS_ATOM_TYPE_SYMBOL) {
+ p->hash_next = rt->atom_hash[h1];
+ rt->atom_hash[h1] = i;
+ if (unlikely(rt->atom_count >= rt->atom_count_resize))
+ JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
+ }
+
+ // JS_DumpAtoms(rt);
+ return i;
+
+ fail:
+ i = JS_ATOM_NULL;
+ done:
+ if (str)
+ js_free_string(rt, str);
+ return i;
+}
+
+/* only works with zero terminated 8 bit strings */
+static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len,
+ int atom_type)
+{
+ JSString *p;
+ p = js_alloc_string_rt(rt, len, 0);
+ if (!p)
+ return JS_ATOM_NULL;
+ memcpy(p->u.str8, str, len);
+ p->u.str8[len] = '\0';
+ return JS_NewAtomImpl(rt, p, atom_type);
+}
+
+static JSAtom JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
+ int atom_type)
+{
+ uint32_t h, h1, i;
+ JSAtomStruct *p;
+
+ h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);
+ h &= JS_ATOM_HASH_MASK;
+ h1 = h & (rt->atom_hash_size - 1);
+ i = rt->atom_hash[h1];
+ while (i != 0) {
+ p = rt->atom_array[i];
+ if (p->hash == h &&
+ p->atom_type == JS_ATOM_TYPE_STRING &&
+ p->len == len &&
+ p->is_wide_char == 0 &&
+ memcmp(p->u.str8, str, len) == 0) {
+ if (!JS_AtomIsConst(i))
+ p->header.ref_count++;
+ return i;
+ }
+ i = p->hash_next;
+ }
+ return JS_ATOM_NULL;
+}
+
+static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
+{
+#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
+ if (unlikely(i == JS_ATOM_NULL)) {
+ p->header.ref_count = INT32_MAX / 2;
+ return;
+ }
+#endif
+ uint32_t i = p->hash_next; /* atom_index */
+ if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
+ JSAtomStruct *p0, *p1;
+ uint32_t h0;
+
+ h0 = p->hash & (rt->atom_hash_size - 1);
+ i = rt->atom_hash[h0];
+ p1 = rt->atom_array[i];
+ if (p1 == p) {
+ rt->atom_hash[h0] = p1->hash_next;
+ } else {
+ for(;;) {
+ assert(i != 0);
+ p0 = p1;
+ i = p1->hash_next;
+ p1 = rt->atom_array[i];
+ if (p1 == p) {
+ p0->hash_next = p1->hash_next;
+ break;
+ }
+ }
+ }
+ }
+ /* insert in free atom list */
+ rt->atom_array[i] = atom_set_free(rt->atom_free_index);
+ rt->atom_free_index = i;
+ /* free the string structure */
+#ifdef DUMP_LEAKS
+ list_del(&p->link);
+#endif
+ js_free_rt(rt, p);
+ rt->atom_count--;
+ assert(rt->atom_count >= 0);
+}
+
+static void JS_FreeAtomImpl(JSRuntime *rt, uint32_t i)
+{
+ JSAtomStruct *p;
+
+ p = rt->atom_array[i];
+ if (--p->header.ref_count > 0)
+ return;
+ JS_FreeAtomStruct(rt, p);
+}
+
+/* Warning: 'p' is freed */
+static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
+{
+ JSRuntime *rt = ctx->rt;
+ uint32_t n;
+ if (is_num_string(&n, p)) {
+ if (n <= JS_ATOM_MAX_INT) {
+ js_free_string(rt, p);
+ return JS_AtomFromUInt32(n);
+ }
+ }
+ /* XXX: should generate an exception */
+ return JS_NewAtomImpl(rt, p, JS_ATOM_TYPE_STRING);
+}
+
+JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
+{
+ JSValue val;
+
+ if (len == 0 || !is_digit(*str)) {
+ JSAtom atom = JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
+ if (atom)
+ return atom;
+ }
+ val = JS_NewStringLen(ctx, str, len);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+ return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
+}
+
+JSAtom JS_NewAtom(JSContext *ctx, const char *str)
+{
+ return JS_NewAtomLen(ctx, str, strlen(str));
+}
+
+JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
+{
+ if (n <= JS_ATOM_MAX_INT) {
+ return JS_AtomFromUInt32(n);
+ } else {
+ char buf[11];
+ JSValue val;
+ snprintf(buf, sizeof(buf), "%u", n);
+ val = JS_NewString(ctx, buf);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val),
+ JS_ATOM_TYPE_STRING);
+ }
+}
+
+static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
+{
+ if ((uint64_t)n <= JS_ATOM_MAX_INT) {
+ return JS_AtomFromUInt32((uint32_t)n);
+ } else {
+ char buf[24];
+ JSValue val;
+ snprintf(buf, sizeof(buf), "%" PRId64 , n);
+ val = JS_NewString(ctx, buf);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val),
+ JS_ATOM_TYPE_STRING);
+ }
+}
+
+/* 'p' is freed */
+static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
+{
+ JSRuntime *rt = ctx->rt;
+ JSAtom atom;
+ atom = JS_NewAtomImpl(rt, p, atom_type);
+ if (atom == JS_ATOM_NULL)
+ return JS_ThrowOutOfMemory(ctx);
+ return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
+}
+
+/* descr must be a non-numeric string atom */
+static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
+ int atom_type)
+{
+ JSRuntime *rt = ctx->rt;
+ JSString *p;
+
+ assert(!JS_AtomIsTaggedInt(descr));
+ assert(descr < rt->atom_size);
+ p = rt->atom_array[descr];
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+ return JS_NewSymbol(ctx, p, atom_type);
+}
+
+#define ATOM_GET_STR_BUF_SIZE 64
+
+/* Should only be used for debug. */
+static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
+ JSAtom atom)
+{
+ if (JS_AtomIsTaggedInt(atom)) {
+ snprintf(buf, buf_size, "%u", JS_AtomToUInt32(atom));
+ } else {
+ JSAtomStruct *p;
+ assert(atom < rt->atom_size);
+ if (atom == JS_ATOM_NULL) {
+ snprintf(buf, buf_size, "<null>");
+ } else {
+ int i, c;
+ char *q;
+ JSString *str;
+
+ q = buf;
+ p = rt->atom_array[atom];
+ assert(!atom_is_free(p));
+ str = p;
+ if (str) {
+ if (!str->is_wide_char) {
+ /* special case ASCII strings */
+ c = 0;
+ for(i = 0; i < str->len; i++) {
+ c |= str->u.str8[i];
+ }
+ if (c < 0x80)
+ return (const char *)str->u.str8;
+ }
+ for(i = 0; i < str->len; i++) {
+ if (str->is_wide_char)
+ c = str->u.str16[i];
+ else
+ c = str->u.str8[i];
+ if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
+ break;
+ if (c < 128) {
+ *q++ = c;
+ } else {
+ q += unicode_to_utf8((uint8_t *)q, c);
+ }
+ }
+ }
+ *q = '\0';
+ }
+ }
+ return buf;
+}
+
+static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
+{
+ return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
+}
+
+static JSValue JS_AtomToValueImpl(JSContext *ctx, JSAtom atom, BOOL force_string)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+
+ if (JS_AtomIsTaggedInt(atom)) {
+ snprintf(buf, sizeof(buf), "%u", JS_AtomToUInt32(atom));
+ return JS_NewString(ctx, buf);
+ } else {
+ JSRuntime *rt = ctx->rt;
+ JSAtomStruct *p;
+ assert(atom < rt->atom_size);
+ p = rt->atom_array[atom];
+ if (p->atom_type == JS_ATOM_TYPE_STRING) {
+ goto ret_string;
+ } else if (force_string) {
+ if (p->len == 0 && p->is_wide_char != 0) {
+ /* no description string */
+ p = rt->atom_array[JS_ATOM_empty_string];
+ }
+ ret_string:
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+ } else {
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
+ }
+ }
+}
+
+JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
+{
+ return JS_AtomToValueImpl(ctx, atom, FALSE);
+}
+
+JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
+{
+ return JS_AtomToValueImpl(ctx, atom, TRUE);
+}
+
+/* return TRUE if the atom is an array index (i.e. 0 <= index <=
+ 2^32-2 and return its value */
+static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
+{
+ if (JS_AtomIsTaggedInt(atom)) {
+ *pval = JS_AtomToUInt32(atom);
+ return TRUE;
+ } else {
+ JSRuntime *rt = ctx->rt;
+ JSAtomStruct *p;
+ uint32_t val;
+
+ assert(atom < rt->atom_size);
+ p = rt->atom_array[atom];
+ if (p->atom_type == JS_ATOM_TYPE_STRING &&
+ is_num_string(&val, p) && val != -1) {
+ *pval = val;
+ return TRUE;
+ } else {
+ *pval = 0;
+ return FALSE;
+ }
+ }
+}
+
+/* This test must be fast if atom is not a numeric index (e.g. a
+ method name). Return JS_UNDEFINED if not a numeric
+ index. JS_EXCEPTION can also be returned. */
+static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
+{
+ JSRuntime *rt = ctx->rt;
+ JSAtomStruct *p1;
+ JSString *p;
+ int c, len, ret;
+ JSValue num, str;
+
+ if (JS_AtomIsTaggedInt(atom))
+ return JS_NewInt32(ctx, JS_AtomToUInt32(atom));
+ assert(atom < rt->atom_size);
+ p1 = rt->atom_array[atom];
+ if (p1->atom_type != JS_ATOM_TYPE_STRING)
+ return JS_UNDEFINED;
+ p = p1;
+ len = p->len;
+ if (p->is_wide_char) {
+ const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;
+ if (r >= r_end)
+ return JS_UNDEFINED;
+ c = *r;
+ if (c == '-') {
+ if (r >= r_end)
+ return JS_UNDEFINED;
+ r++;
+ c = *r;
+ /* -0 case is specific */
+ if (c == '0' && len == 2)
+ goto minus_zero;
+ }
+ /* XXX: should test NaN, but the tests do not check it */
+ if (!is_num(c)) {
+ /* XXX: String should be normalized, therefore 8-bit only */
+ const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };
+ if (!(c =='I' && (r_end - r) == 8 &&
+ !memcmp(r + 1, nfinity16, sizeof(nfinity16))))
+ return JS_UNDEFINED;
+ }
+ } else {
+ const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;
+ if (r >= r_end)
+ return JS_UNDEFINED;
+ c = *r;
+ if (c == '-') {
+ if (r >= r_end)
+ return JS_UNDEFINED;
+ r++;
+ c = *r;
+ /* -0 case is specific */
+ if (c == '0' && len == 2) {
+ minus_zero:
+ return JS_NewFloat64Impl(ctx, -0.0);
+ }
+ }
+ if (!is_num(c)) {
+ if (!(c =='I' && (r_end - r) == 8 &&
+ !memcmp(r + 1, "nfinity", 7)))
+ return JS_UNDEFINED;
+ }
+ }
+ /* XXX: bignum: would be better to only accept integer to avoid
+ relying on current floating point precision */
+ /* this is ECMA CanonicalNumericIndexString primitive */
+ num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
+ if (JS_IsException(num))
+ return num;
+ str = JS_ToString(ctx, num);
+ if (JS_IsException(str)) {
+ JS_FreeValue(ctx, num);
+ return str;
+ }
+ ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
+ JS_FreeValue(ctx, str);
+ if (ret == 0) {
+ return num;
+ } else {
+ JS_FreeValue(ctx, num);
+ return JS_UNDEFINED;
+ }
+}
+
+/* return -1 if exception or TRUE/FALSE */
+static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
+{
+ JSValue num;
+ num = JS_AtomIsNumericIndex1(ctx, atom);
+ if (likely(JS_IsUndefined(num)))
+ return FALSE;
+ if (JS_IsException(num))
+ return -1;
+ JS_FreeValue(ctx, num);
+ return TRUE;
+}
+
+void JS_FreeAtom(JSContext *ctx, JSAtom v)
+{
+ if (!JS_AtomIsConst(v))
+ JS_FreeAtomImpl(ctx->rt, v);
+}
+
+void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
+{
+ if (!JS_AtomIsConst(v))
+ JS_FreeAtomImpl(rt, v);
+}
+
+/* return TRUE if 'v' is a symbol with a string description */
+static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
+{
+ JSRuntime *rt;
+ JSAtomStruct *p;
+
+ rt = ctx->rt;
+ if (JS_AtomIsTaggedInt(v))
+ return FALSE;
+ p = rt->atom_array[v];
+ return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
+ p->hash == JS_ATOM_HASH_SYMBOL) ||
+ p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
+ !(p->len == 0 && p->is_wide_char != 0));
+}
+
+static maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ const char *p;
+ int i;
+
+ /* XXX: should handle embedded null characters */
+ /* XXX: should move encoding code to JS_AtomGetStr */
+ p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);
+ for (i = 0; p[i]; i++) {
+ int c = (unsigned char)p[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
+ break;
+ }
+ if (i > 0 && p[i] == '\0') {
+ printf("%s", p);
+ } else {
+ putchar('"');
+ printf("%.*s", i, p);
+ for (; p[i]; i++) {
+ int c = (unsigned char)p[i];
+ if (c == '\"' || c == '\\') {
+ putchar('\\');
+ putchar(c);
+ } else if (c >= ' ' && c <= 126) {
+ putchar(c);
+ } else if (c == '\n') {
+ putchar('\\');
+ putchar('n');
+ } else {
+ printf("\\u%04x", c);
+ }
+ }
+ putchar('\"');
+ }
+}
+
+/* free with JS_FreeCString() */
+const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
+{
+ JSValue str;
+ const char *cstr;
+
+ str = JS_AtomToString(ctx, atom);
+ if (JS_IsException(str))
+ return NULL;
+ cstr = JS_ToCString(ctx, str);
+ JS_FreeValue(ctx, str);
+ return cstr;
+}
+
+/* return a string atom containing name concatenated with str1 */
+static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
+{
+ JSValue str;
+ JSAtom atom;
+ const char *cstr;
+ char *cstr2;
+ size_t len, len1;
+
+ str = JS_AtomToString(ctx, name);
+ if (JS_IsException(str))
+ return JS_ATOM_NULL;
+ cstr = JS_ToCStringLen(ctx, &len, str);
+ if (!cstr)
+ goto fail;
+ len1 = strlen(str1);
+ cstr2 = js_malloc(ctx, len + len1 + 1);
+ if (!cstr2)
+ goto fail;
+ memcpy(cstr2, cstr, len);
+ memcpy(cstr2 + len, str1, len1);
+ cstr2[len + len1] = '\0';
+ atom = JS_NewAtomLen(ctx, cstr2, len + len1);
+ js_free(ctx, cstr2);
+ JS_FreeCString(ctx, cstr);
+ JS_FreeValue(ctx, str);
+ return atom;
+ fail:
+ JS_FreeCString(ctx, cstr);
+ JS_FreeValue(ctx, str);
+ return JS_ATOM_NULL;
+}
+
+static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
+{
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%u", n);
+ return js_atom_concat_str(ctx, name, buf);
+}
+
+static inline BOOL JS_IsEmptyString(JSValueConst v)
+{
+ return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
+}
+
+/* JSClass support */
+
+/* a new class ID is allocated if *pclass_id != 0 */
+JSClassID JS_NewClassID(JSClassID *pclass_id)
+{
+ JSClassID class_id;
+ /* XXX: make it thread safe */
+ class_id = *pclass_id;
+ if (class_id == 0) {
+ class_id = js_class_id_alloc++;
+ *pclass_id = class_id;
+ }
+ return class_id;
+}
+
+BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)
+{
+ return (class_id < rt->class_count &&
+ rt->class_array[class_id].class_id != 0);
+}
+
+/* create a new object internal class. Return -1 if error, 0 if
+ OK. The finalizer can be NULL if none is needed. */
+static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
+ const JSClassDef *class_def, JSAtom name)
+{
+ int new_size, i;
+ JSClass *cl, *new_class_array;
+ struct list_head *el;
+
+ if (class_id >= (1 << 16))
+ return -1;
+ if (class_id < rt->class_count &&
+ rt->class_array[class_id].class_id != 0)
+ return -1;
+
+ if (class_id >= rt->class_count) {
+ new_size = max_int(JS_CLASS_INIT_COUNT,
+ max_int(class_id + 1, rt->class_count * 3 / 2));
+
+ /* reallocate the context class prototype array, if any */
+ list_for_each(el, &rt->context_list) {
+ JSContext *ctx = list_entry(el, JSContext, link);
+ JSValue *new_tab;
+ new_tab = js_realloc_rt(rt, ctx->class_proto,
+ sizeof(ctx->class_proto[0]) * new_size);
+ if (!new_tab)
+ return -1;
+ for(i = rt->class_count; i < new_size; i++)
+ new_tab[i] = JS_NULL;
+ ctx->class_proto = new_tab;
+ }
+ /* reallocate the class array */
+ new_class_array = js_realloc_rt(rt, rt->class_array,
+ sizeof(JSClass) * new_size);
+ if (!new_class_array)
+ return -1;
+ memset(new_class_array + rt->class_count, 0,
+ (new_size - rt->class_count) * sizeof(JSClass));
+ rt->class_array = new_class_array;
+ rt->class_count = new_size;
+ }
+ cl = &rt->class_array[class_id];
+ cl->class_id = class_id;
+ cl->class_name = JS_DupAtomRT(rt, name);
+ cl->finalizer = class_def->finalizer;
+ cl->gc_mark = class_def->gc_mark;
+ cl->call = class_def->call;
+ cl->exotic = class_def->exotic;
+ return 0;
+}
+
+int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
+{
+ int ret, len;
+ JSAtom name;
+
+ len = strlen(class_def->class_name);
+ name = JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
+ if (name == JS_ATOM_NULL) {
+ name = JS_NewAtomInitImpl(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
+ if (name == JS_ATOM_NULL)
+ return -1;
+ }
+ ret = JS_NewClass1(rt, class_id, class_def, name);
+ JS_FreeAtomRT(rt, name);
+ return ret;
+}
+
+static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)
+{
+ JSString *str;
+
+ if (len <= 0) {
+ return JS_AtomToString(ctx, JS_ATOM_empty_string);
+ }
+ str = js_alloc_string(ctx, len, 0);
+ if (!str)
+ return JS_EXCEPTION;
+ memcpy(str->u.str8, buf, len);
+ str->u.str8[len] = '\0';
+ return JS_MKPTR(JS_TAG_STRING, str);
+}
+
+static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)
+{
+ JSString *str;
+ str = js_alloc_string(ctx, len, 1);
+ if (!str)
+ return JS_EXCEPTION;
+ memcpy(str->u.str16, buf, len * 2);
+ return JS_MKPTR(JS_TAG_STRING, str);
+}
+
+static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
+{
+ if (c < 0x100) {
+ uint8_t ch8 = c;
+ return js_new_string8(ctx, &ch8, 1);
+ } else {
+ uint16_t ch16 = c;
+ return js_new_string16(ctx, &ch16, 1);
+ }
+}
+
+static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
+{
+ int len = end - start;
+ if (start == 0 && end == p->len) {
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+ }
+ if (p->is_wide_char && len > 0) {
+ JSString *str;
+ int i;
+ uint16_t c = 0;
+ for (i = start; i < end; i++) {
+ c |= p->u.str16[i];
+ }
+ if (c > 0xFF)
+ return js_new_string16(ctx, p->u.str16 + start, len);
+
+ str = js_alloc_string(ctx, len, 0);
+ if (!str)
+ return JS_EXCEPTION;
+ for (i = 0; i < len; i++) {
+ str->u.str8[i] = p->u.str16[start + i];
+ }
+ str->u.str8[len] = '\0';
+ return JS_MKPTR(JS_TAG_STRING, str);
+ } else {
+ return js_new_string8(ctx, p->u.str8 + start, len);
+ }
+}
+
+typedef struct StringBuffer {
+ JSContext *ctx;
+ JSString *str;
+ int len;
+ int size;
+ int is_wide_char;
+ int error_status;
+} StringBuffer;
+
+/* It is valid to call string_buffer_end() and all string_buffer functions even
+ if string_buffer_init() or another string_buffer function returns an error.
+ If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
+ */
+static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,
+ int is_wide)
+{
+ s->ctx = ctx;
+ s->size = size;
+ s->len = 0;
+ s->is_wide_char = is_wide;
+ s->error_status = 0;
+ s->str = js_alloc_string(ctx, size, is_wide);
+ if (unlikely(!s->str)) {
+ s->size = 0;
+ return s->error_status = -1;
+ }
+#ifdef DUMP_LEAKS
+ /* the StringBuffer may reallocate the JSString, only link it at the end */
+ list_del(&s->str->link);
+#endif
+ return 0;
+}
+
+static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
+{
+ return string_buffer_init2(ctx, s, size, 0);
+}
+
+static void string_buffer_free(StringBuffer *s)
+{
+ js_free(s->ctx, s->str);
+ s->str = NULL;
+}
+
+static int string_buffer_set_error(StringBuffer *s)
+{
+ js_free(s->ctx, s->str);
+ s->str = NULL;
+ s->size = 0;
+ s->len = 0;
+ return s->error_status = -1;
+}
+
+static no_inline int string_buffer_widen(StringBuffer *s, int size)
+{
+ JSString *str;
+ size_t slack;
+ int i;
+
+ if (s->error_status)
+ return -1;
+
+ str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
+ if (!str)
+ return string_buffer_set_error(s);
+ size += slack >> 1;
+ for(i = s->len; i-- > 0;) {
+ str->u.str16[i] = str->u.str8[i];
+ }
+ s->is_wide_char = 1;
+ s->size = size;
+ s->str = str;
+ return 0;
+}
+
+static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)
+{
+ JSString *new_str;
+ int new_size;
+ size_t new_size_bytes, slack;
+
+ if (s->error_status)
+ return -1;
+
+ if (new_len > JS_STRING_LEN_MAX) {
+ JS_ThrowInternalError(s->ctx, "string too long");
+ return string_buffer_set_error(s);
+ }
+ new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
+ if (!s->is_wide_char && c >= 0x100) {
+ return string_buffer_widen(s, new_size);
+ }
+ new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
+ new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
+ if (!new_str)
+ return string_buffer_set_error(s);
+ new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
+ s->size = new_size;
+ s->str = new_str;
+ return 0;
+}
+
+static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
+{
+ if (unlikely(s->len >= s->size)) {
+ if (string_buffer_realloc(s, s->len + 1, c))
+ return -1;
+ }
+ if (s->is_wide_char) {
+ s->str->u.str16[s->len++] = c;
+ } else if (c < 0x100) {
+ s->str->u.str8[s->len++] = c;
+ } else {
+ if (string_buffer_widen(s, s->size))
+ return -1;
+ s->str->u.str16[s->len++] = c;
+ }
+ return 0;
+}
+
+/* 0 <= c <= 0xff */
+static int string_buffer_putc8(StringBuffer *s, uint32_t c)
+{
+ if (unlikely(s->len >= s->size)) {
+ if (string_buffer_realloc(s, s->len + 1, c))
+ return -1;
+ }
+ if (s->is_wide_char) {
+ s->str->u.str16[s->len++] = c;
+ } else {
+ s->str->u.str8[s->len++] = c;
+ }
+ return 0;
+}
+
+/* 0 <= c <= 0xffff */
+static int string_buffer_putc16(StringBuffer *s, uint32_t c)
+{
+ if (likely(s->len < s->size)) {
+ if (s->is_wide_char) {
+ s->str->u.str16[s->len++] = c;
+ return 0;
+ } else if (c < 0x100) {
+ s->str->u.str8[s->len++] = c;
+ return 0;
+ }
+ }
+ return string_buffer_putc_slow(s, c);
+}
+
+/* 0 <= c <= 0x10ffff */
+static int string_buffer_putc(StringBuffer *s, uint32_t c)
+{
+ if (unlikely(c >= 0x10000)) {
+ /* surrogate pair */
+ c -= 0x10000;
+ if (string_buffer_putc16(s, (c >> 10) + 0xd800))
+ return -1;
+ c = (c & 0x3ff) + 0xdc00;
+ }
+ return string_buffer_putc16(s, c);
+}
+
+static int string_get(const JSString *p, int idx) {
+ return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
+}
+
+static int string_getc(const JSString *p, int *pidx)
+{
+ int idx, c, c1;
+ idx = *pidx;
+ if (p->is_wide_char) {
+ c = p->u.str16[idx++];
+ if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {
+ c1 = p->u.str16[idx];
+ if (c1 >= 0xdc00 && c1 < 0xe000) {
+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
+ idx++;
+ }
+ }
+ } else {
+ c = p->u.str8[idx++];
+ }
+ *pidx = idx;
+ return c;
+}
+
+static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
+{
+ int i;
+
+ if (s->len + len > s->size) {
+ if (string_buffer_realloc(s, s->len + len, 0))
+ return -1;
+ }
+ if (s->is_wide_char) {
+ for (i = 0; i < len; i++) {
+ s->str->u.str16[s->len + i] = p[i];
+ }
+ s->len += len;
+ } else {
+ memcpy(&s->str->u.str8[s->len], p, len);
+ s->len += len;
+ }
+ return 0;
+}
+
+static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
+{
+ int c = 0, i;
+
+ for (i = 0; i < len; i++) {
+ c |= p[i];
+ }
+ if (s->len + len > s->size) {
+ if (string_buffer_realloc(s, s->len + len, c))
+ return -1;
+ } else if (!s->is_wide_char && c >= 0x100) {
+ if (string_buffer_widen(s, s->size))
+ return -1;
+ }
+ if (s->is_wide_char) {
+ memcpy(&s->str->u.str16[s->len], p, len << 1);
+ s->len += len;
+ } else {
+ for (i = 0; i < len; i++) {
+ s->str->u.str8[s->len + i] = p[i];
+ }
+ s->len += len;
+ }
+ return 0;
+}
+
+/* appending an ASCII string */
+static int string_buffer_puts8(StringBuffer *s, const char *str)
+{
+ return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
+}
+
+static int string_buffer_concat(StringBuffer *s, const JSString *p,
+ uint32_t from, uint32_t to)
+{
+ if (to <= from)
+ return 0;
+ if (p->is_wide_char)
+ return string_buffer_write16(s, p->u.str16 + from, to - from);
+ else
+ return string_buffer_write8(s, p->u.str8 + from, to - from);
+}
+
+static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
+{
+ JSString *p;
+ JSValue v1;
+ int res;
+
+ if (s->error_status) {
+ /* prevent exception overload */
+ return -1;
+ }
+ if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
+ v1 = JS_ToString(s->ctx, v);
+ if (JS_IsException(v1))
+ return string_buffer_set_error(s);
+ p = JS_VALUE_GET_STRING(v1);
+ res = string_buffer_concat(s, p, 0, p->len);
+ JS_FreeValue(s->ctx, v1);
+ return res;
+ }
+ p = JS_VALUE_GET_STRING(v);
+ return string_buffer_concat(s, p, 0, p->len);
+}
+
+static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
+{
+ JSString *p;
+ int res;
+
+ if (s->error_status) {
+ /* prevent exception overload */
+ JS_FreeValue(s->ctx, v);
+ return -1;
+ }
+ if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
+ v = JS_ToStringFree(s->ctx, v);
+ if (JS_IsException(v))
+ return string_buffer_set_error(s);
+ }
+ p = JS_VALUE_GET_STRING(v);
+ res = string_buffer_concat(s, p, 0, p->len);
+ JS_FreeValue(s->ctx, v);
+ return res;
+}
+
+static int string_buffer_fill(StringBuffer *s, int c, int count)
+{
+ /* XXX: optimize */
+ if (s->len + count > s->size) {
+ if (string_buffer_realloc(s, s->len + count, c))
+ return -1;
+ }
+ while (count-- > 0) {
+ if (string_buffer_putc16(s, c))
+ return -1;
+ }
+ return 0;
+}
+
+static JSValue string_buffer_end(StringBuffer *s)
+{
+ JSString *str;
+ str = s->str;
+ if (s->error_status)
+ return JS_EXCEPTION;
+ if (s->len == 0) {
+ js_free(s->ctx, str);
+ s->str = NULL;
+ return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
+ }
+ if (s->len < s->size) {
+ /* smaller size so js_realloc should not fail, but OK if it does */
+ /* XXX: should add some slack to avoid unnecessary calls */
+ /* XXX: might need to use malloc+free to ensure smaller size */
+ str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
+ (s->len << s->is_wide_char) + 1 - s->is_wide_char);
+ if (str == NULL)
+ str = s->str;
+ s->str = str;
+ }
+ if (!s->is_wide_char)
+ str->u.str8[s->len] = 0;
+#ifdef DUMP_LEAKS
+ list_add_tail(&str->link, &s->ctx->rt->string_list);
+#endif
+ str->is_wide_char = s->is_wide_char;
+ str->len = s->len;
+ s->str = NULL;
+ return JS_MKPTR(JS_TAG_STRING, str);
+}
+
+/* create a string from a UTF-8 buffer */
+JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
+{
+ const uint8_t *p, *p_end, *p_start, *p_next;
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+ size_t len1;
+
+ p_start = (const uint8_t *)buf;
+ p_end = p_start + buf_len;
+ p = p_start;
+ while (p < p_end && *p < 128)
+ p++;
+ len1 = p - p_start;
+ if (len1 > JS_STRING_LEN_MAX)
+ return JS_ThrowInternalError(ctx, "string too long");
+ if (p == p_end) {
+ /* ASCII string */
+ return js_new_string8(ctx, (const uint8_t *)buf, buf_len);
+ } else {
+ if (string_buffer_init(ctx, b, buf_len))
+ goto fail;
+ string_buffer_write8(b, p_start, len1);
+ while (p < p_end) {
+ if (*p < 128) {
+ string_buffer_putc8(b, *p++);
+ } else {
+ /* parse utf-8 sequence, return 0xFFFFFFFF for error */
+ c = unicode_from_utf8(p, p_end - p, &p_next);
+ if (c < 0x10000) {
+ p = p_next;
+ } else if (c <= 0x10FFFF) {
+ p = p_next;
+ /* surrogate pair */
+ c -= 0x10000;
+ string_buffer_putc16(b, (c >> 10) + 0xd800);
+ c = (c & 0x3ff) + 0xdc00;
+ } else {
+ /* invalid char */
+ c = 0xfffd;
+ /* skip the invalid chars */
+ /* XXX: seems incorrect. Why not just use c = *p++; ? */
+ while (p < p_end && (*p >= 0x80 && *p < 0xc0))
+ p++;
+ if (p < p_end) {
+ p++;
+ while (p < p_end && (*p >= 0x80 && *p < 0xc0))
+ p++;
+ }
+ }
+ string_buffer_putc16(b, c);
+ }
+ }
+ }
+ return string_buffer_end(b);
+
+ fail:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,
+ JSValue str2, const char *str3)
+{
+ StringBuffer b_s, *b = &b_s;
+ int len1, len3;
+ JSString *p;
+
+ if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {
+ str2 = JS_ToStringFree(ctx, str2);
+ if (JS_IsException(str2))
+ goto fail;
+ }
+ p = JS_VALUE_GET_STRING(str2);
+ len1 = strlen(str1);
+ len3 = strlen(str3);
+
+ if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))
+ goto fail;
+
+ string_buffer_write8(b, (const uint8_t *)str1, len1);
+ string_buffer_concat(b, p, 0, p->len);
+ string_buffer_write8(b, (const uint8_t *)str3, len3);
+
+ JS_FreeValue(ctx, str2);
+ return string_buffer_end(b);
+
+ fail:
+ JS_FreeValue(ctx, str2);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_NewString(JSContext *ctx, const char *str)
+{
+ return JS_NewStringLen(ctx, str, strlen(str));
+}
+
+JSValue JS_NewAtomString(JSContext *ctx, const char *str)
+{
+ JSAtom atom = JS_NewAtom(ctx, str);
+ if (atom == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+ JSValue val = JS_AtomToString(ctx, atom);
+ JS_FreeAtom(ctx, atom);
+ return val;
+}
+
+/* return (NULL, 0) if exception. */
+/* return pointer into a JSString with a live ref_count */
+/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */
+const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)
+{
+ JSValue val;
+ JSString *str, *str_new;
+ int pos, len, c, c1;
+ uint8_t *q;
+
+ if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {
+ val = JS_ToString(ctx, val1);
+ if (JS_IsException(val))
+ goto fail;
+ } else {
+ val = JS_DupValue(ctx, val1);
+ }
+
+ str = JS_VALUE_GET_STRING(val);
+ len = str->len;
+ if (!str->is_wide_char) {
+ const uint8_t *src = str->u.str8;
+ int count;
+
+ /* count the number of non-ASCII characters */
+ /* Scanning the whole string is required for ASCII strings,
+ and computing the number of non-ASCII bytes is less expensive
+ than testing each byte, hence this method is faster for ASCII
+ strings, which is the most common case.
+ */
+ count = 0;
+ for (pos = 0; pos < len; pos++) {
+ count += src[pos] >> 7;
+ }
+ if (count == 0) {
+ if (plen)
+ *plen = len;
+ return (const char *)src;
+ }
+ str_new = js_alloc_string(ctx, len + count, 0);
+ if (!str_new)
+ goto fail;
+ q = str_new->u.str8;
+ for (pos = 0; pos < len; pos++) {
+ c = src[pos];
+ if (c < 0x80) {
+ *q++ = c;
+ } else {
+ *q++ = (c >> 6) | 0xc0;
+ *q++ = (c & 0x3f) | 0x80;
+ }
+ }
+ } else {
+ const uint16_t *src = str->u.str16;
+ /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may
+ produce 4 bytes but use 2 code points.
+ */
+ str_new = js_alloc_string(ctx, len * 3, 0);
+ if (!str_new)
+ goto fail;
+ q = str_new->u.str8;
+ pos = 0;
+ while (pos < len) {
+ c = src[pos++];
+ if (c < 0x80) {
+ *q++ = c;
+ } else {
+ if (c >= 0xd800 && c < 0xdc00) {
+ if (pos < len && !cesu8) {
+ c1 = src[pos];
+ if (c1 >= 0xdc00 && c1 < 0xe000) {
+ pos++;
+ /* surrogate pair */
+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
+ } else {
+ /* Keep unmatched surrogate code points */
+ /* c = 0xfffd; */ /* error */
+ }
+ } else {
+ /* Keep unmatched surrogate code points */
+ /* c = 0xfffd; */ /* error */
+ }
+ }
+ q += unicode_to_utf8(q, c);
+ }
+ }
+ }
+
+ *q = '\0';
+ str_new->len = q - str_new->u.str8;
+ JS_FreeValue(ctx, val);
+ if (plen)
+ *plen = str_new->len;
+ return (const char *)str_new->u.str8;
+ fail:
+ if (plen)
+ *plen = 0;
+ return NULL;
+}
+
+void JS_FreeCString(JSContext *ctx, const char *ptr)
+{
+ JSString *p;
+ if (!ptr)
+ return;
+ /* purposely removing constness */
+ p = (JSString *)(void *)(ptr - offsetof(JSString, u));
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+}
+
+static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
+{
+ int c, i;
+ for(i = 0; i < len; i++) {
+ c = src1[i] - src2[i];
+ if (c != 0)
+ return c;
+ }
+ return 0;
+}
+
+static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
+{
+ int c, i;
+ for(i = 0; i < len; i++) {
+ c = src1[i] - src2[i];
+ if (c != 0)
+ return c;
+ }
+ return 0;
+}
+
+static int js_string_memcmp(const JSString *p1, const JSString *p2, int len)
+{
+ int res;
+
+ if (likely(!p1->is_wide_char)) {
+ if (likely(!p2->is_wide_char))
+ res = memcmp(p1->u.str8, p2->u.str8, len);
+ else
+ res = -memcmp16_8(p2->u.str16, p1->u.str8, len);
+ } else {
+ if (!p2->is_wide_char)
+ res = memcmp16_8(p1->u.str16, p2->u.str8, len);
+ else
+ res = memcmp16(p1->u.str16, p2->u.str16, len);
+ }
+ return res;
+}
+
+/* return < 0, 0 or > 0 */
+static int js_string_compare(JSContext *ctx,
+ const JSString *p1, const JSString *p2)
+{
+ int res, len;
+ len = min_int(p1->len, p2->len);
+ res = js_string_memcmp(p1, p2, len);
+ if (res == 0) {
+ if (p1->len == p2->len)
+ res = 0;
+ else if (p1->len < p2->len)
+ res = -1;
+ else
+ res = 1;
+ }
+ return res;
+}
+
+static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)
+{
+ if (p->is_wide_char) {
+ memcpy(dst, p->u.str16 + offset, len * 2);
+ } else {
+ const uint8_t *src1 = p->u.str8 + offset;
+ int i;
+
+ for(i = 0; i < len; i++)
+ dst[i] = src1[i];
+ }
+}
+
+static JSValue JS_ConcatString1(JSContext *ctx,
+ const JSString *p1, const JSString *p2)
+{
+ JSString *p;
+ uint32_t len;
+ int is_wide_char;
+
+ len = p1->len + p2->len;
+ if (len > JS_STRING_LEN_MAX)
+ return JS_ThrowInternalError(ctx, "string too long");
+ is_wide_char = p1->is_wide_char | p2->is_wide_char;
+ p = js_alloc_string(ctx, len, is_wide_char);
+ if (!p)
+ return JS_EXCEPTION;
+ if (!is_wide_char) {
+ memcpy(p->u.str8, p1->u.str8, p1->len);
+ memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);
+ p->u.str8[len] = '\0';
+ } else {
+ copy_str16(p->u.str16, p1, 0, p1->len);
+ copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);
+ }
+ return JS_MKPTR(JS_TAG_STRING, p);
+}
+
+/* op1 and op2 are converted to strings. For convience, op1 or op2 =
+ JS_EXCEPTION are accepted and return JS_EXCEPTION. */
+static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
+{
+ JSValue ret;
+ JSString *p1, *p2;
+
+ if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) {
+ op1 = JS_ToStringFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ return JS_EXCEPTION;
+ }
+ }
+ if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) {
+ op2 = JS_ToStringFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ return JS_EXCEPTION;
+ }
+ }
+ p1 = JS_VALUE_GET_STRING(op1);
+ p2 = JS_VALUE_GET_STRING(op2);
+
+ /* XXX: could also check if p1 is empty */
+ if (p2->len == 0) {
+ goto ret_op1;
+ }
+ if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char
+ && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) {
+ /* Concatenate in place in available space at the end of p1 */
+ if (p1->is_wide_char) {
+ memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);
+ p1->len += p2->len;
+ } else {
+ memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);
+ p1->len += p2->len;
+ p1->u.str8[p1->len] = '\0';
+ }
+ ret_op1:
+ JS_FreeValue(ctx, op2);
+ return op1;
+ }
+ ret = JS_ConcatString1(ctx, p1, p2);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return ret;
+}
+
+/* Shape support */
+
+static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
+{
+ return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
+ prop_size * sizeof(JSShapeProperty);
+}
+
+static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
+{
+ return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
+}
+
+static inline uint32_t *prop_hash_end(JSShape *sh)
+{
+ return (uint32_t *)sh;
+}
+
+static inline void *get_alloc_from_shape(JSShape *sh)
+{
+ return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
+}
+
+static inline JSShapeProperty *get_shape_prop(JSShape *sh)
+{
+ return sh->prop;
+}
+
+static int init_shape_hash(JSRuntime *rt)
+{
+ rt->shape_hash_bits = 4; /* 16 shapes */
+ rt->shape_hash_size = 1 << rt->shape_hash_bits;
+ rt->shape_hash_count = 0;
+ rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
+ rt->shape_hash_size);
+ if (!rt->shape_hash)
+ return -1;
+ return 0;
+}
+
+/* same magic hash multiplier as the Linux kernel */
+static uint32_t shape_hash(uint32_t h, uint32_t val)
+{
+ return (h + val) * 0x9e370001;
+}
+
+/* truncate the shape hash to 'hash_bits' bits */
+static uint32_t get_shape_hash(uint32_t h, int hash_bits)
+{
+ return h >> (32 - hash_bits);
+}
+
+static uint32_t shape_initial_hash(JSObject *proto)
+{
+ uint32_t h;
+ h = shape_hash(1, (uintptr_t)proto);
+ if (sizeof(proto) > 4)
+ h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
+ return h;
+}
+
+static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
+{
+ int new_shape_hash_size, i;
+ uint32_t h;
+ JSShape **new_shape_hash, *sh, *sh_next;
+
+ new_shape_hash_size = 1 << new_shape_hash_bits;
+ new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
+ new_shape_hash_size);
+ if (!new_shape_hash)
+ return -1;
+ for(i = 0; i < rt->shape_hash_size; i++) {
+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
+ sh_next = sh->shape_hash_next;
+ h = get_shape_hash(sh->hash, new_shape_hash_bits);
+ sh->shape_hash_next = new_shape_hash[h];
+ new_shape_hash[h] = sh;
+ }
+ }
+ js_free_rt(rt, rt->shape_hash);
+ rt->shape_hash_bits = new_shape_hash_bits;
+ rt->shape_hash_size = new_shape_hash_size;
+ rt->shape_hash = new_shape_hash;
+ return 0;
+}
+
+static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
+{
+ uint32_t h;
+ h = get_shape_hash(sh->hash, rt->shape_hash_bits);
+ sh->shape_hash_next = rt->shape_hash[h];
+ rt->shape_hash[h] = sh;
+ rt->shape_hash_count++;
+}
+
+static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
+{
+ uint32_t h;
+ JSShape **psh;
+
+ h = get_shape_hash(sh->hash, rt->shape_hash_bits);
+ psh = &rt->shape_hash[h];
+ while (*psh != sh)
+ psh = &(*psh)->shape_hash_next;
+ *psh = sh->shape_hash_next;
+ rt->shape_hash_count--;
+}
+
+/* create a new empty shape with prototype 'proto' */
+static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
+ int hash_size, int prop_size)
+{
+ JSRuntime *rt = ctx->rt;
+ void *sh_alloc;
+ JSShape *sh;
+
+ /* resize the shape hash table if necessary */
+ if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
+ resize_shape_hash(rt, rt->shape_hash_bits + 1);
+ }
+
+ sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
+ if (!sh_alloc)
+ return NULL;
+ sh = get_shape_from_alloc(sh_alloc, hash_size);
+ sh->header.ref_count = 1;
+ add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
+ if (proto)
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
+ sh->proto = proto;
+ memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
+ hash_size);
+ sh->prop_hash_mask = hash_size - 1;
+ sh->prop_size = prop_size;
+ sh->prop_count = 0;
+ sh->deleted_prop_count = 0;
+
+ /* insert in the hash table */
+ sh->hash = shape_initial_hash(proto);
+ sh->is_hashed = TRUE;
+ sh->has_small_array_index = FALSE;
+ js_shape_hash_link(ctx->rt, sh);
+ return sh;
+}
+
+static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
+{
+ return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
+ JS_PROP_INITIAL_SIZE);
+}
+
+/* The shape is cloned. The new shape is not inserted in the shape
+ hash table */
+static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
+{
+ JSShape *sh;
+ void *sh_alloc, *sh_alloc1;
+ size_t size;
+ JSShapeProperty *pr;
+ uint32_t i, hash_size;
+
+ hash_size = sh1->prop_hash_mask + 1;
+ size = get_shape_size(hash_size, sh1->prop_size);
+ sh_alloc = js_malloc(ctx, size);
+ if (!sh_alloc)
+ return NULL;
+ sh_alloc1 = get_alloc_from_shape(sh1);
+ memcpy(sh_alloc, sh_alloc1, size);
+ sh = get_shape_from_alloc(sh_alloc, hash_size);
+ sh->header.ref_count = 1;
+ add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
+ sh->is_hashed = FALSE;
+ if (sh->proto) {
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
+ }
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
+ JS_DupAtom(ctx, pr->atom);
+ }
+ return sh;
+}
+
+static JSShape *js_dup_shape(JSShape *sh)
+{
+ sh->header.ref_count++;
+ return sh;
+}
+
+static void js_free_shape0(JSRuntime *rt, JSShape *sh)
+{
+ uint32_t i;
+ JSShapeProperty *pr;
+
+ assert(sh->header.ref_count == 0);
+ if (sh->is_hashed)
+ js_shape_hash_unlink(rt, sh);
+ if (sh->proto != NULL) {
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
+ }
+ pr = get_shape_prop(sh);
+ for(i = 0; i < sh->prop_count; i++) {
+ JS_FreeAtomRT(rt, pr->atom);
+ pr++;
+ }
+ remove_gc_object(&sh->header);
+ js_free_rt(rt, get_alloc_from_shape(sh));
+}
+
+static void js_free_shape(JSRuntime *rt, JSShape *sh)
+{
+ if (unlikely(--sh->header.ref_count <= 0)) {
+ js_free_shape0(rt, sh);
+ }
+}
+
+static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
+{
+ if (sh)
+ js_free_shape(rt, sh);
+}
+
+/* make space to hold at least 'count' properties */
+static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
+ JSObject *p, uint32_t count)
+{
+ JSShape *sh;
+ uint32_t new_size, new_hash_size, new_hash_mask, i;
+ JSShapeProperty *pr;
+ void *sh_alloc;
+ intptr_t h;
+
+ sh = *psh;
+ new_size = max_int(count, sh->prop_size * 3 / 2);
+ /* Reallocate prop array first to avoid crash or size inconsistency
+ in case of memory allocation failure */
+ if (p) {
+ JSProperty *new_prop;
+ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
+ if (unlikely(!new_prop))
+ return -1;
+ p->prop = new_prop;
+ }
+ new_hash_size = sh->prop_hash_mask + 1;
+ while (new_hash_size < new_size)
+ new_hash_size = 2 * new_hash_size;
+ if (new_hash_size != (sh->prop_hash_mask + 1)) {
+ JSShape *old_sh;
+ /* resize the hash table and the properties */
+ old_sh = sh;
+ sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
+ if (!sh_alloc)
+ return -1;
+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);
+ list_del(&old_sh->header.link);
+ /* copy all the fields and the properties */
+ memcpy(sh, old_sh,
+ sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ new_hash_mask = new_hash_size - 1;
+ sh->prop_hash_mask = new_hash_mask;
+ memset(prop_hash_end(sh) - new_hash_size, 0,
+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);
+ for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
+ if (pr->atom != JS_ATOM_NULL) {
+ h = ((uintptr_t)pr->atom & new_hash_mask);
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = i + 1;
+ }
+ }
+ js_free(ctx, get_alloc_from_shape(old_sh));
+ } else {
+ /* only resize the properties */
+ list_del(&sh->header.link);
+ sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),
+ get_shape_size(new_hash_size, new_size));
+ if (unlikely(!sh_alloc)) {
+ /* insert again in the GC list */
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ return -1;
+ }
+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ }
+ *psh = sh;
+ sh->prop_size = new_size;
+ return 0;
+}
+
+/* remove the deleted properties. */
+static int compact_properties(JSContext *ctx, JSObject *p)
+{
+ JSShape *sh, *old_sh;
+ void *sh_alloc;
+ intptr_t h;
+ uint32_t new_hash_size, i, j, new_hash_mask, new_size;
+ JSShapeProperty *old_pr, *pr;
+ JSProperty *prop, *new_prop;
+
+ sh = p->shape;
+ assert(!sh->is_hashed);
+
+ new_size = max_int(JS_PROP_INITIAL_SIZE,
+ sh->prop_count - sh->deleted_prop_count);
+ assert(new_size <= sh->prop_size);
+
+ new_hash_size = sh->prop_hash_mask + 1;
+ while ((new_hash_size / 2) >= new_size)
+ new_hash_size = new_hash_size / 2;
+ new_hash_mask = new_hash_size - 1;
+
+ /* resize the hash table and the properties */
+ old_sh = sh;
+ sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
+ if (!sh_alloc)
+ return -1;
+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);
+ list_del(&old_sh->header.link);
+ memcpy(sh, old_sh, sizeof(JSShape));
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+
+ memset(prop_hash_end(sh) - new_hash_size, 0,
+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);
+
+ j = 0;
+ old_pr = old_sh->prop;
+ pr = sh->prop;
+ prop = p->prop;
+ for(i = 0; i < sh->prop_count; i++) {
+ if (old_pr->atom != JS_ATOM_NULL) {
+ pr->atom = old_pr->atom;
+ pr->flags = old_pr->flags;
+ h = ((uintptr_t)old_pr->atom & new_hash_mask);
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = j + 1;
+ prop[j] = prop[i];
+ j++;
+ pr++;
+ }
+ old_pr++;
+ }
+ assert(j == (sh->prop_count - sh->deleted_prop_count));
+ sh->prop_hash_mask = new_hash_mask;
+ sh->prop_size = new_size;
+ sh->deleted_prop_count = 0;
+ sh->prop_count = j;
+
+ p->shape = sh;
+ js_free(ctx, get_alloc_from_shape(old_sh));
+
+ /* reduce the size of the object properties */
+ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
+ if (new_prop)
+ p->prop = new_prop;
+ return 0;
+}
+
+static int add_shape_property(JSContext *ctx, JSShape **psh,
+ JSObject *p, JSAtom atom, int prop_flags)
+{
+ JSRuntime *rt = ctx->rt;
+ JSShape *sh = *psh;
+ JSShapeProperty *pr, *prop;
+ uint32_t hash_mask, new_shape_hash = 0;
+ intptr_t h;
+
+ /* update the shape hash */
+ if (sh->is_hashed) {
+ js_shape_hash_unlink(rt, sh);
+ new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
+ }
+
+ if (unlikely(sh->prop_count >= sh->prop_size)) {
+ if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
+ /* in case of error, reinsert in the hash table.
+ sh is still valid if resize_properties() failed */
+ if (sh->is_hashed)
+ js_shape_hash_link(rt, sh);
+ return -1;
+ }
+ sh = *psh;
+ }
+ if (sh->is_hashed) {
+ sh->hash = new_shape_hash;
+ js_shape_hash_link(rt, sh);
+ }
+ /* Initialize the new shape property.
+ The object property at p->prop[sh->prop_count] is uninitialized */
+ prop = get_shape_prop(sh);
+ pr = &prop[sh->prop_count++];
+ pr->atom = JS_DupAtom(ctx, atom);
+ pr->flags = prop_flags;
+ sh->has_small_array_index |= JS_AtomIsTaggedInt(atom);
+ /* add in hash table */
+ hash_mask = sh->prop_hash_mask;
+ h = atom & hash_mask;
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = sh->prop_count;
+ return 0;
+}
+
+/* find a hashed empty shape matching the prototype. Return NULL if
+ not found */
+static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
+{
+ JSShape *sh1;
+ uint32_t h, h1;
+
+ h = shape_initial_hash(proto);
+ h1 = get_shape_hash(h, rt->shape_hash_bits);
+ for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
+ if (sh1->hash == h &&
+ sh1->proto == proto &&
+ sh1->prop_count == 0) {
+ return sh1;
+ }
+ }
+ return NULL;
+}
+
+/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
+ not found */
+static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
+ JSAtom atom, int prop_flags)
+{
+ JSShape *sh1;
+ uint32_t h, h1, i, n;
+
+ h = sh->hash;
+ h = shape_hash(h, atom);
+ h = shape_hash(h, prop_flags);
+ h1 = get_shape_hash(h, rt->shape_hash_bits);
+ for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
+ /* we test the hash first so that the rest is done only if the
+ shapes really match */
+ if (sh1->hash == h &&
+ sh1->proto == sh->proto &&
+ sh1->prop_count == ((n = sh->prop_count) + 1)) {
+ for(i = 0; i < n; i++) {
+ if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||
+ unlikely(sh1->prop[i].flags != sh->prop[i].flags))
+ goto next;
+ }
+ if (unlikely(sh1->prop[n].atom != atom) ||
+ unlikely(sh1->prop[n].flags != prop_flags))
+ goto next;
+ return sh1;
+ }
+ next: ;
+ }
+ return NULL;
+}
+
+static maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
+{
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ int j;
+
+ /* XXX: should output readable class prototype */
+ printf("%5d %3d%c %14p %5d %5d", i,
+ sh->header.ref_count, " *"[sh->is_hashed],
+ (void *)sh->proto, sh->prop_size, sh->prop_count);
+ for(j = 0; j < sh->prop_count; j++) {
+ printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
+ sh->prop[j].atom));
+ }
+ printf("\n");
+}
+
+static maybe_unused void JS_DumpShapes(JSRuntime *rt)
+{
+ int i;
+ JSShape *sh;
+ struct list_head *el;
+ JSObject *p;
+ JSGCObjectHeader *gp;
+
+ printf("JSShapes: {\n");
+ printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
+ for(i = 0; i < rt->shape_hash_size; i++) {
+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
+ JS_DumpShape(rt, i, sh);
+ assert(sh->is_hashed);
+ }
+ }
+ /* dump non-hashed shapes */
+ list_for_each(el, &rt->gc_obj_list) {
+ gp = list_entry(el, JSGCObjectHeader, link);
+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ p = (JSObject *)gp;
+ if (!p->shape->is_hashed) {
+ JS_DumpShape(rt, -1, p->shape);
+ }
+ }
+ }
+ printf("}\n");
+}
+
+static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
+{
+ JSObject *p;
+
+ js_trigger_gc(ctx->rt, sizeof(JSObject));
+ p = js_malloc(ctx, sizeof(JSObject));
+ if (unlikely(!p))
+ goto fail;
+ p->class_id = class_id;
+ p->extensible = TRUE;
+ p->free_mark = 0;
+ p->is_exotic = 0;
+ p->fast_array = 0;
+ p->is_constructor = 0;
+ p->is_uncatchable_error = 0;
+ p->tmp_mark = 0;
+ p->is_HTMLDDA = 0;
+ p->first_weak_ref = NULL;
+ p->u.opaque = NULL;
+ p->shape = sh;
+ p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
+ if (unlikely(!p->prop)) {
+ js_free(ctx, p);
+ fail:
+ js_free_shape(ctx->rt, sh);
+ return JS_EXCEPTION;
+ }
+
+ switch(class_id) {
+ case JS_CLASS_OBJECT:
+ break;
+ case JS_CLASS_ARRAY:
+ {
+ JSProperty *pr;
+ p->is_exotic = 1;
+ p->fast_array = 1;
+ p->u.array.u.values = NULL;
+ p->u.array.count = 0;
+ p->u.array.u1.size = 0;
+ /* the length property is always the first one */
+ if (likely(sh == ctx->array_shape)) {
+ pr = &p->prop[0];
+ } else {
+ /* only used for the first array */
+ /* cannot fail */
+ pr = add_property(ctx, p, JS_ATOM_length,
+ JS_PROP_WRITABLE | JS_PROP_LENGTH);
+ }
+ pr->u.value = JS_NewInt32(ctx, 0);
+ }
+ break;
+ case JS_CLASS_C_FUNCTION:
+ p->prop[0].u.value = JS_UNDEFINED;
+ break;
+ case JS_CLASS_ARGUMENTS:
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ case JS_CLASS_FLOAT64_ARRAY:
+ p->is_exotic = 1;
+ p->fast_array = 1;
+ p->u.array.u.ptr = NULL;
+ p->u.array.count = 0;
+ break;
+ case JS_CLASS_DATAVIEW:
+ p->u.array.u.ptr = NULL;
+ p->u.array.count = 0;
+ break;
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+ case JS_CLASS_SYMBOL:
+ case JS_CLASS_DATE:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ p->u.object_data = JS_UNDEFINED;
+ goto set_exotic;
+ case JS_CLASS_REGEXP:
+ p->u.regexp.pattern = NULL;
+ p->u.regexp.bytecode = NULL;
+ goto set_exotic;
+ default:
+ set_exotic:
+ if (ctx->rt->class_array[class_id].exotic) {
+ p->is_exotic = 1;
+ }
+ break;
+ }
+ p->header.ref_count = 1;
+ add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
+ return JS_MKPTR(JS_TAG_OBJECT, p);
+}
+
+static JSObject *get_proto_obj(JSValueConst proto_val)
+{
+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)
+ return NULL;
+ else
+ return JS_VALUE_GET_OBJ(proto_val);
+}
+
+/* WARNING: proto must be an object or JS_NULL */
+JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
+ JSClassID class_id)
+{
+ JSShape *sh;
+ JSObject *proto;
+
+ proto = get_proto_obj(proto_val);
+ sh = find_hashed_shape_proto(ctx->rt, proto);
+ if (likely(sh)) {
+ sh = js_dup_shape(sh);
+ } else {
+ sh = js_new_shape(ctx, proto);
+ if (!sh)
+ return JS_EXCEPTION;
+ }
+ return JS_NewObjectFromShape(ctx, sh, class_id);
+}
+
+#if 0
+static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(obj);
+ switch(p->class_id) {
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+ case JS_CLASS_SYMBOL:
+ case JS_CLASS_DATE:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_UNDEFINED;
+}
+#endif
+
+static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)
+{
+ JSObject *p;
+
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(obj);
+ switch(p->class_id) {
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+ case JS_CLASS_SYMBOL:
+ case JS_CLASS_DATE:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ JS_FreeValue(ctx, p->u.object_data);
+ p->u.object_data = val;
+ return 0;
+ }
+ }
+ JS_FreeValue(ctx, val);
+ if (!JS_IsException(obj))
+ JS_ThrowTypeError(ctx, "invalid object type");
+ return -1;
+}
+
+JSValue JS_NewObjectClass(JSContext *ctx, int class_id)
+{
+ return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);
+}
+
+JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)
+{
+ return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);
+}
+
+JSValue JS_NewArray(JSContext *ctx)
+{
+ return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),
+ JS_CLASS_ARRAY);
+}
+
+JSValue JS_NewObject(JSContext *ctx)
+{
+ /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
+ return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
+}
+
+static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,
+ JSAtom name, int len)
+{
+ /* ES6 feature non compatible with ES5.1: length is configurable */
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),
+ JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,
+ JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);
+}
+
+static BOOL js_class_has_bytecode(JSClassID class_id)
+{
+ return (class_id == JS_CLASS_BYTECODE_FUNCTION ||
+ class_id == JS_CLASS_GENERATOR_FUNCTION ||
+ class_id == JS_CLASS_ASYNC_FUNCTION ||
+ class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);
+}
+
+/* return NULL without exception if not a function or no bytecode */
+static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return NULL;
+ p = JS_VALUE_GET_OBJ(val);
+ if (!js_class_has_bytecode(p->class_id))
+ return NULL;
+ return p->u.func.function_bytecode;
+}
+
+static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst home_obj)
+{
+ JSObject *p, *p1;
+ JSFunctionBytecode *b;
+
+ if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
+ return;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ if (!js_class_has_bytecode(p->class_id))
+ return;
+ b = p->u.func.function_bytecode;
+ if (b->need_home_object) {
+ p1 = p->u.func.home_object;
+ if (p1) {
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ }
+ if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)
+ p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));
+ else
+ p1 = NULL;
+ p->u.func.home_object = p1;
+ }
+}
+
+static JSValue js_get_function_name(JSContext *ctx, JSAtom name)
+{
+ JSValue name_str;
+
+ name_str = JS_AtomToString(ctx, name);
+ if (JS_AtomSymbolHasDescription(ctx, name)) {
+ name_str = JS_ConcatString3(ctx, "[", name_str, "]");
+ }
+ return name_str;
+}
+
+/* Modify the name of a method according to the atom and
+ 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and
+ JS_PROP_HAS_SET. Also set the home object of the method.
+ Return < 0 if exception. */
+static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,
+ JSAtom name, int flags, JSValueConst home_obj)
+{
+ JSValue name_str;
+
+ name_str = js_get_function_name(ctx, name);
+ if (flags & JS_PROP_HAS_GET) {
+ name_str = JS_ConcatString3(ctx, "get ", name_str, "");
+ } else if (flags & JS_PROP_HAS_SET) {
+ name_str = JS_ConcatString3(ctx, "set ", name_str, "");
+ }
+ if (JS_IsException(name_str))
+ return -1;
+ if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,
+ JS_PROP_CONFIGURABLE) < 0)
+ return -1;
+ js_method_set_home_object(ctx, func_obj, home_obj);
+ return 0;
+}
+
+/* Note: at least 'length' arguments will be readable in 'argv' */
+static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,
+ const char *name,
+ int length, JSCFunctionEnum cproto, int magic,
+ JSValueConst proto_val)
+{
+ JSValue func_obj;
+ JSObject *p;
+ JSAtom name_atom;
+
+ func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);
+ if (JS_IsException(func_obj))
+ return func_obj;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->u.cfunc.realm = JS_DupContext(ctx);
+ p->u.cfunc.c_function.generic = func;
+ p->u.cfunc.length = length;
+ p->u.cfunc.cproto = cproto;
+ p->u.cfunc.magic = magic;
+ p->is_constructor = (cproto == JS_CFUNC_constructor ||
+ cproto == JS_CFUNC_constructor_magic ||
+ cproto == JS_CFUNC_constructor_or_func ||
+ cproto == JS_CFUNC_constructor_or_func_magic);
+ if (!name)
+ name = "";
+ name_atom = JS_NewAtom(ctx, name);
+ js_function_set_properties(ctx, func_obj, name_atom, length);
+ JS_FreeAtom(ctx, name_atom);
+ return func_obj;
+}
+
+/* Note: at least 'length' arguments will be readable in 'argv' */
+JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
+ const char *name,
+ int length, JSCFunctionEnum cproto, int magic)
+{
+ return JS_NewCFunction3(ctx, func, name, length, cproto, magic,
+ ctx->function_proto);
+}
+
+typedef struct JSCFunctionDataRecord {
+ JSCFunctionData *func;
+ uint8_t length;
+ uint8_t data_len;
+ uint16_t magic;
+ JSValue data[0];
+} JSCFunctionDataRecord;
+
+static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
+ int i;
+
+ if (s) {
+ for(i = 0; i < s->data_len; i++) {
+ JS_FreeValueRT(rt, s->data[i]);
+ }
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
+ int i;
+
+ if (s) {
+ for(i = 0; i < s->data_len; i++) {
+ JS_MarkValue(rt, s->data[i], mark_func);
+ }
+ }
+}
+
+static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
+ JSValueConst *arg_buf;
+ int i;
+
+ /* XXX: could add the function on the stack for debug */
+ if (unlikely(argc < s->length)) {
+ arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
+ for(i = 0; i < argc; i++)
+ arg_buf[i] = argv[i];
+ for(i = argc; i < s->length; i++)
+ arg_buf[i] = JS_UNDEFINED;
+ } else {
+ arg_buf = argv;
+ }
+
+ return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);
+}
+
+JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
+ int length, int magic, int data_len,
+ JSValueConst *data)
+{
+ JSCFunctionDataRecord *s;
+ JSValue func_obj;
+ int i;
+
+ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_C_FUNCTION_DATA);
+ if (JS_IsException(func_obj))
+ return func_obj;
+ s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));
+ if (!s) {
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+ }
+ s->func = func;
+ s->length = length;
+ s->data_len = data_len;
+ s->magic = magic;
+ for(i = 0; i < data_len; i++)
+ s->data[i] = JS_DupValue(ctx, data[i]);
+ JS_SetOpaque(func_obj, s);
+ js_function_set_properties(ctx, func_obj,
+ JS_ATOM_empty_string, length);
+ return func_obj;
+}
+
+static JSContext *js_autoinit_get_realm(JSProperty *pr)
+{
+ return (JSContext *)(pr->u.init.realm_and_id & ~3);
+}
+
+static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
+{
+ return pr->u.init.realm_and_id & 3;
+}
+
+static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
+{
+ JS_FreeContext(js_autoinit_get_realm(pr));
+}
+
+static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
+ JS_MarkFunc *mark_func)
+{
+ mark_func(rt, &js_autoinit_get_realm(pr)->header);
+}
+
+static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
+{
+ if (unlikely(prop_flags & JS_PROP_TMASK)) {
+ if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ if (pr->u.getset.getter)
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+ if (pr->u.getset.setter)
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+ } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ free_var_ref(rt, pr->u.var_ref);
+ } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ js_autoinit_free(rt, pr);
+ }
+ } else {
+ JS_FreeValueRT(rt, pr->u.value);
+ }
+}
+
+static force_inline JSShapeProperty *find_own_property1(JSObject *p,
+ JSAtom atom)
+{
+ JSShape *sh;
+ JSShapeProperty *pr, *prop;
+ intptr_t h;
+ sh = p->shape;
+ h = (uintptr_t)atom & sh->prop_hash_mask;
+ h = prop_hash_end(sh)[-h - 1];
+ prop = get_shape_prop(sh);
+ while (h) {
+ pr = &prop[h - 1];
+ if (likely(pr->atom == atom)) {
+ return pr;
+ }
+ h = pr->hash_next;
+ }
+ return NULL;
+}
+
+static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
+ JSObject *p,
+ JSAtom atom)
+{
+ JSShape *sh;
+ JSShapeProperty *pr, *prop;
+ intptr_t h;
+ sh = p->shape;
+ h = (uintptr_t)atom & sh->prop_hash_mask;
+ h = prop_hash_end(sh)[-h - 1];
+ prop = get_shape_prop(sh);
+ while (h) {
+ pr = &prop[h - 1];
+ if (likely(pr->atom == atom)) {
+ *ppr = &p->prop[h - 1];
+ /* the compiler should be able to assume that pr != NULL here */
+ return pr;
+ }
+ h = pr->hash_next;
+ }
+ *ppr = NULL;
+ return NULL;
+}
+
+/* indicate that the object may be part of a function prototype cycle */
+static void set_cycle_flag(JSContext *ctx, JSValueConst obj)
+{
+}
+
+static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
+{
+ if (var_ref) {
+ assert(var_ref->header.ref_count > 0);
+ if (--var_ref->header.ref_count == 0) {
+ if (var_ref->is_detached) {
+ JS_FreeValueRT(rt, var_ref->value);
+ remove_gc_object(&var_ref->header);
+ } else {
+ list_del(&var_ref->header.link); /* still on the stack */
+ }
+ js_free_rt(rt, var_ref);
+ }
+ }
+}
+
+static void js_array_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ int i;
+
+ for(i = 0; i < p->u.array.count; i++) {
+ JS_FreeValueRT(rt, p->u.array.u.values[i]);
+ }
+ js_free_rt(rt, p->u.array.u.values);
+}
+
+static void js_array_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ int i;
+
+ for(i = 0; i < p->u.array.count; i++) {
+ JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
+ }
+}
+
+static void js_object_data_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JS_FreeValueRT(rt, p->u.object_data);
+ p->u.object_data = JS_UNDEFINED;
+}
+
+static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JS_MarkValue(rt, p->u.object_data, mark_func);
+}
+
+static void js_c_function_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+
+ if (p->u.cfunc.realm)
+ JS_FreeContext(p->u.cfunc.realm);
+}
+
+static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+
+ if (p->u.cfunc.realm)
+ mark_func(rt, &p->u.cfunc.realm->header);
+}
+
+static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p1, *p = JS_VALUE_GET_OBJ(val);
+ JSFunctionBytecode *b;
+ JSVarRef **var_refs;
+ int i;
+
+ p1 = p->u.func.home_object;
+ if (p1) {
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));
+ }
+ b = p->u.func.function_bytecode;
+ if (b) {
+ var_refs = p->u.func.var_refs;
+ if (var_refs) {
+ for(i = 0; i < b->closure_var_count; i++)
+ free_var_ref(rt, var_refs[i]);
+ js_free_rt(rt, var_refs);
+ }
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));
+ }
+}
+
+static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSVarRef **var_refs = p->u.func.var_refs;
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ int i;
+
+ if (p->u.func.home_object) {
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
+ mark_func);
+ }
+ if (b) {
+ if (var_refs) {
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSVarRef *var_ref = var_refs[i];
+ if (var_ref && var_ref->is_detached) {
+ mark_func(rt, &var_ref->header);
+ }
+ }
+ }
+ /* must mark the function bytecode because template objects may be
+ part of a cycle */
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);
+ }
+}
+
+static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSBoundFunction *bf = p->u.bound_function;
+ int i;
+
+ JS_FreeValueRT(rt, bf->func_obj);
+ JS_FreeValueRT(rt, bf->this_val);
+ for(i = 0; i < bf->argc; i++) {
+ JS_FreeValueRT(rt, bf->argv[i]);
+ }
+ js_free_rt(rt, bf);
+}
+
+static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSBoundFunction *bf = p->u.bound_function;
+ int i;
+
+ JS_MarkValue(rt, bf->func_obj, mark_func);
+ JS_MarkValue(rt, bf->this_val, mark_func);
+ for(i = 0; i < bf->argc; i++)
+ JS_MarkValue(rt, bf->argv[i], mark_func);
+}
+
+static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSForInIterator *it = p->u.for_in_iterator;
+ JS_FreeValueRT(rt, it->obj);
+ js_free_rt(rt, it);
+}
+
+static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSForInIterator *it = p->u.for_in_iterator;
+ JS_MarkValue(rt, it->obj, mark_func);
+}
+
+static void free_object(JSRuntime *rt, JSObject *p)
+{
+ int i;
+ JSClassFinalizer *finalizer;
+ JSShape *sh;
+ JSShapeProperty *pr;
+
+ p->free_mark = 1; /* used to tell the object is invalid when
+ freeing cycles */
+ /* free all the fields */
+ sh = p->shape;
+ pr = get_shape_prop(sh);
+ for(i = 0; i < sh->prop_count; i++) {
+ free_property(rt, &p->prop[i], pr->flags);
+ pr++;
+ }
+ js_free_rt(rt, p->prop);
+ /* as an optimization we destroy the shape immediately without
+ putting it in gc_zero_ref_count_list */
+ js_free_shape(rt, sh);
+
+ /* fail safe */
+ p->shape = NULL;
+ p->prop = NULL;
+
+ if (unlikely(p->first_weak_ref)) {
+ reset_weak_ref(rt, p);
+ }
+
+ finalizer = rt->class_array[p->class_id].finalizer;
+ if (finalizer)
+ (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));
+
+ /* fail safe */
+ p->class_id = 0;
+ p->u.opaque = NULL;
+ p->u.func.var_refs = NULL;
+ p->u.func.home_object = NULL;
+
+ remove_gc_object(&p->header);
+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {
+ list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
+ } else {
+ js_free_rt(rt, p);
+ }
+}
+
+static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
+{
+ switch(gp->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_JS_OBJECT:
+ free_object(rt, (JSObject *)gp);
+ break;
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+ free_function_bytecode(rt, (JSFunctionBytecode *)gp);
+ break;
+ default:
+ abort();
+ }
+}
+
+static void free_zero_refcount(JSRuntime *rt)
+{
+ struct list_head *el;
+ JSGCObjectHeader *p;
+
+ rt->gc_phase = JS_GC_PHASE_DECREF;
+ for(;;) {
+ el = rt->gc_zero_ref_count_list.next;
+ if (el == &rt->gc_zero_ref_count_list)
+ break;
+ p = list_entry(el, JSGCObjectHeader, link);
+ assert(p->ref_count == 0);
+ free_gc_object(rt, p);
+ }
+ rt->gc_phase = JS_GC_PHASE_NONE;
+}
+
+/* called with the ref_count of 'v' reaches zero. */
+static void JS_FreeValueRTImpl(JSRuntime *rt, JSValue v)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(v);
+
+#ifdef DUMP_FREE
+ {
+ printf("Freeing ");
+ if (tag == JS_TAG_OBJECT) {
+ JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));
+ } else {
+ JS_DumpValueShort(rt, v);
+ printf("\n");
+ }
+ }
+#endif
+
+ switch(tag) {
+ case JS_TAG_STRING:
+ {
+ JSString *p = JS_VALUE_GET_STRING(v);
+ if (p->atom_type) {
+ JS_FreeAtomStruct(rt, p);
+ } else {
+#ifdef DUMP_LEAKS
+ list_del(&p->link);
+#endif
+ js_free_rt(rt, p);
+ }
+ }
+ break;
+ case JS_TAG_OBJECT:
+ case JS_TAG_FUNCTION_BYTECODE:
+ {
+ JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
+ if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
+ list_del(&p->link);
+ list_add(&p->link, &rt->gc_zero_ref_count_list);
+ if (rt->gc_phase == JS_GC_PHASE_NONE) {
+ free_zero_refcount(rt);
+ }
+ }
+ }
+ break;
+ case JS_TAG_MODULE:
+ abort(); /* never freed here */
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *bf = JS_VALUE_GET_PTR(v);
+ bf_delete(&bf->num);
+ js_free_rt(rt, bf);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *bf = JS_VALUE_GET_PTR(v);
+ bfdec_delete(&bf->num);
+ js_free_rt(rt, bf);
+ }
+ break;
+#endif
+ case JS_TAG_SYMBOL:
+ {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(v);
+ JS_FreeAtomStruct(rt, p);
+ }
+ break;
+ default:
+ printf("__JS_FreeValue: unknown tag=%d\n", tag);
+ abort();
+ }
+}
+
+static void JS_FreeValueImpl(JSContext *ctx, JSValue v)
+{
+ JS_FreeValueRTImpl(ctx->rt, v);
+}
+
+/* garbage collection */
+
+static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
+ JSGCObjectTypeEnum type)
+{
+ h->mark = 0;
+ h->gc_obj_type = type;
+ list_add_tail(&h->link, &rt->gc_obj_list);
+}
+
+static void remove_gc_object(JSGCObjectHeader *h)
+{
+ list_del(&h->link);
+}
+
+void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
+{
+ if (JS_VALUE_HAS_REF_COUNT(val)) {
+ switch(JS_VALUE_GET_TAG(val)) {
+ case JS_TAG_OBJECT:
+ case JS_TAG_FUNCTION_BYTECODE:
+ mark_func(rt, JS_VALUE_GET_PTR(val));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
+ JS_MarkFunc *mark_func)
+{
+ switch(gp->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_JS_OBJECT:
+ {
+ JSObject *p = (JSObject *)gp;
+ JSShapeProperty *prs;
+ JSShape *sh;
+ int i;
+ sh = p->shape;
+ mark_func(rt, &sh->header);
+ /* mark all the fields */
+ prs = get_shape_prop(sh);
+ for(i = 0; i < sh->prop_count; i++) {
+ JSProperty *pr = &p->prop[i];
+ if (prs->atom != JS_ATOM_NULL) {
+ if (prs->flags & JS_PROP_TMASK) {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ if (pr->u.getset.getter)
+ mark_func(rt, &pr->u.getset.getter->header);
+ if (pr->u.getset.setter)
+ mark_func(rt, &pr->u.getset.setter->header);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ if (pr->u.var_ref->is_detached) {
+ /* Note: the tag does not matter
+ provided it is a GC object */
+ mark_func(rt, &pr->u.var_ref->header);
+ }
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ js_autoinit_mark(rt, pr, mark_func);
+ }
+ } else {
+ JS_MarkValue(rt, pr->u.value, mark_func);
+ }
+ }
+ prs++;
+ }
+
+ if (p->class_id != JS_CLASS_OBJECT) {
+ JSClassGCMark *gc_mark;
+ gc_mark = rt->class_array[p->class_id].gc_mark;
+ if (gc_mark)
+ gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
+ }
+ }
+ break;
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+ /* the template objects can be part of a cycle */
+ {
+ JSFunctionBytecode *b = (JSFunctionBytecode *)gp;
+ int i;
+ for(i = 0; i < b->cpool_count; i++) {
+ JS_MarkValue(rt, b->cpool[i], mark_func);
+ }
+ if (b->realm)
+ mark_func(rt, &b->realm->header);
+ }
+ break;
+ case JS_GC_OBJ_TYPE_VAR_REF:
+ {
+ JSVarRef *var_ref = (JSVarRef *)gp;
+ /* only detached variable referenced are taken into account */
+ assert(var_ref->is_detached);
+ JS_MarkValue(rt, *var_ref->pvalue, mark_func);
+ }
+ break;
+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
+ {
+ JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;
+ if (s->is_active)
+ async_func_mark(rt, &s->func_state, mark_func);
+ JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
+ JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
+ }
+ break;
+ case JS_GC_OBJ_TYPE_SHAPE:
+ {
+ JSShape *sh = (JSShape *)gp;
+ if (sh->proto != NULL) {
+ mark_func(rt, &sh->proto->header);
+ }
+ }
+ break;
+ case JS_GC_OBJ_TYPE_JS_CONTEXT:
+ {
+ JSContext *ctx = (JSContext *)gp;
+ JS_MarkContext(rt, ctx, mark_func);
+ }
+ break;
+ default:
+ abort();
+ }
+}
+
+static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)
+{
+ assert(p->ref_count > 0);
+ p->ref_count--;
+ if (p->ref_count == 0 && p->mark == 1) {
+ list_del(&p->link);
+ list_add_tail(&p->link, &rt->tmp_obj_list);
+ }
+}
+
+static void gc_decref(JSRuntime *rt)
+{
+ struct list_head *el, *el1;
+ JSGCObjectHeader *p;
+
+ init_list_head(&rt->tmp_obj_list);
+
+ /* decrement the refcount of all the children of all the GC
+ objects and move the GC objects with zero refcount to
+ tmp_obj_list */
+ list_for_each_safe(el, el1, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ assert(p->mark == 0);
+ mark_children(rt, p, gc_decref_child);
+ p->mark = 1;
+ if (p->ref_count == 0) {
+ list_del(&p->link);
+ list_add_tail(&p->link, &rt->tmp_obj_list);
+ }
+ }
+}
+
+static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)
+{
+ p->ref_count++;
+ if (p->ref_count == 1) {
+ /* ref_count was 0: remove from tmp_obj_list and add at the
+ end of gc_obj_list */
+ list_del(&p->link);
+ list_add_tail(&p->link, &rt->gc_obj_list);
+ p->mark = 0; /* reset the mark for the next GC call */
+ }
+}
+
+static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
+{
+ p->ref_count++;
+}
+
+static void gc_scan(JSRuntime *rt)
+{
+ struct list_head *el;
+ JSGCObjectHeader *p;
+
+ /* keep the objects with a refcount > 0 and their children. */
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ assert(p->ref_count > 0);
+ p->mark = 0; /* reset the mark for the next GC call */
+ mark_children(rt, p, gc_scan_incref_child);
+ }
+
+ /* restore the refcount of the objects to be deleted. */
+ list_for_each(el, &rt->tmp_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ mark_children(rt, p, gc_scan_incref_child2);
+ }
+}
+
+static void gc_free_cycles(JSRuntime *rt)
+{
+ struct list_head *el, *el1;
+ JSGCObjectHeader *p;
+#ifdef DUMP_GC_FREE
+ BOOL header_done = FALSE;
+#endif
+
+ rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;
+
+ for(;;) {
+ el = rt->tmp_obj_list.next;
+ if (el == &rt->tmp_obj_list)
+ break;
+ p = list_entry(el, JSGCObjectHeader, link);
+ /* Only need to free the GC object associated with JS
+ values. The rest will be automatically removed because they
+ must be referenced by them. */
+ switch(p->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_JS_OBJECT:
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+#ifdef DUMP_GC_FREE
+ if (!header_done) {
+ printf("Freeing cycles:\n");
+ JS_DumpObjectHeader(rt);
+ header_done = TRUE;
+ }
+ JS_DumpGCObject(rt, p);
+#endif
+ free_gc_object(rt, p);
+ break;
+ default:
+ list_del(&p->link);
+ list_add_tail(&p->link, &rt->gc_zero_ref_count_list);
+ break;
+ }
+ }
+ rt->gc_phase = JS_GC_PHASE_NONE;
+
+ list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
+ p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
+ js_free_rt(rt, p);
+ }
+
+ init_list_head(&rt->gc_zero_ref_count_list);
+}
+
+void JS_RunGC(JSRuntime *rt)
+{
+ /* decrement the reference of the children of each object. mark =
+ 1 after this pass. */
+ gc_decref(rt);
+
+ /* keep the GC objects with a non zero refcount and their childs */
+ gc_scan(rt);
+
+ /* free the GC objects in a cycle */
+ gc_free_cycles(rt);
+}
+
+/* Return false if not an object or if the object has already been
+ freed (zombie objects are visible in finalizers when freeing
+ cycles). */
+BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)
+{
+ JSObject *p;
+ if (!JS_IsObject(obj))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ return !p->free_mark;
+}
+
+/* Compute memory used by various object types */
+/* XXX: poor man's approach to handling multiply referenced objects */
+typedef struct JSMemoryUsage_helper {
+ double memory_used_count;
+ double str_count;
+ double str_size;
+ int64_t js_func_count;
+ double js_func_size;
+ int64_t js_func_code_size;
+ int64_t js_func_pc2line_count;
+ int64_t js_func_pc2line_size;
+} JSMemoryUsage_helper;
+
+static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
+
+static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)
+{
+ if (!str->atom_type) { /* atoms are handled separately */
+ double s_ref_count = str->header.ref_count;
+ hp->str_count += 1 / s_ref_count;
+ hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
+ 1 - str->is_wide_char) / s_ref_count);
+ }
+}
+
+static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)
+{
+ int memory_used_count, js_func_size, i;
+
+ memory_used_count = 0;
+ js_func_size = offsetof(JSFunctionBytecode, debug);
+ if (b->vardefs) {
+ js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);
+ }
+ if (b->cpool) {
+ js_func_size += b->cpool_count * sizeof(*b->cpool);
+ for (i = 0; i < b->cpool_count; i++) {
+ JSValueConst val = b->cpool[i];
+ compute_value_size(val, hp);
+ }
+ }
+ if (b->closure_var) {
+ js_func_size += b->closure_var_count * sizeof(*b->closure_var);
+ }
+ if (!b->read_only_bytecode && b->byte_code_buf) {
+ hp->js_func_code_size += b->byte_code_len;
+ }
+ if (b->has_debug) {
+ js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);
+ if (b->debug.source) {
+ memory_used_count++;
+ js_func_size += b->debug.source_len + 1;
+ }
+ if (b->debug.pc2line_len) {
+ memory_used_count++;
+ hp->js_func_pc2line_count += 1;
+ hp->js_func_pc2line_size += b->debug.pc2line_len;
+ }
+ }
+ hp->js_func_size += js_func_size;
+ hp->js_func_count += 1;
+ hp->memory_used_count += memory_used_count;
+}
+
+static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
+{
+ switch(JS_VALUE_GET_TAG(val)) {
+ case JS_TAG_STRING:
+ compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ case JS_TAG_BIG_DECIMAL:
+ /* should track JSBigFloat usage */
+ break;
+#endif
+ }
+}
+
+void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
+{
+ struct list_head *el, *el1;
+ int i;
+ JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
+
+ memset(s, 0, sizeof(*s));
+ s->malloc_count = rt->malloc_state.malloc_count;
+ s->malloc_size = rt->malloc_state.malloc_size;
+ s->malloc_limit = rt->malloc_state.malloc_limit;
+
+ s->memory_used_count = 2; /* rt + rt->class_array */
+ s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
+
+ list_for_each(el, &rt->context_list) {
+ JSContext *ctx = list_entry(el, JSContext, link);
+ JSShape *sh = ctx->array_shape;
+ s->memory_used_count += 2; /* ctx + ctx->class_proto */
+ s->memory_used_size += sizeof(JSContext) +
+ sizeof(JSValue) * rt->class_count;
+ s->binary_object_count += ctx->binary_object_count;
+ s->binary_object_size += ctx->binary_object_size;
+
+ /* the hashed shapes are counted separately */
+ if (sh && !sh->is_hashed) {
+ int hash_size = sh->prop_hash_mask + 1;
+ s->shape_count++;
+ s->shape_size += get_shape_size(hash_size, sh->prop_size);
+ }
+ list_for_each(el1, &ctx->loaded_modules) {
+ JSModuleDef *m = list_entry(el1, JSModuleDef, link);
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*m);
+ if (m->req_module_entries) {
+ s->memory_used_count += 1;
+ s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);
+ }
+ if (m->export_entries) {
+ s->memory_used_count += 1;
+ s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);
+ for (i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {
+ /* potential multiple count */
+ s->memory_used_count += 1;
+ compute_value_size(me->u.local.var_ref->value, hp);
+ }
+ }
+ }
+ if (m->star_export_entries) {
+ s->memory_used_count += 1;
+ s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);
+ }
+ if (m->import_entries) {
+ s->memory_used_count += 1;
+ s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);
+ }
+ compute_value_size(m->module_ns, hp);
+ compute_value_size(m->func_obj, hp);
+ }
+ }
+
+ list_for_each(el, &rt->gc_obj_list) {
+ JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
+ JSObject *p;
+ JSShape *sh;
+ JSShapeProperty *prs;
+
+ /* XXX: could count the other GC object types too */
+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
+ compute_bytecode_size((JSFunctionBytecode *)gp, hp);
+ continue;
+ } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
+ continue;
+ }
+ p = (JSObject *)gp;
+ sh = p->shape;
+ s->obj_count++;
+ if (p->prop) {
+ s->memory_used_count++;
+ s->prop_size += sh->prop_size * sizeof(*p->prop);
+ s->prop_count += sh->prop_count;
+ prs = get_shape_prop(sh);
+ for(i = 0; i < sh->prop_count; i++) {
+ JSProperty *pr = &p->prop[i];
+ if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {
+ compute_value_size(pr->u.value, hp);
+ }
+ prs++;
+ }
+ }
+ /* the hashed shapes are counted separately */
+ if (!sh->is_hashed) {
+ int hash_size = sh->prop_hash_mask + 1;
+ s->shape_count++;
+ s->shape_size += get_shape_size(hash_size, sh->prop_size);
+ }
+
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY: /* u.array | length */
+ case JS_CLASS_ARGUMENTS: /* u.array | length */
+ s->array_count++;
+ if (p->fast_array) {
+ s->fast_array_count++;
+ if (p->u.array.u.values) {
+ s->memory_used_count++;
+ s->memory_used_size += p->u.array.count *
+ sizeof(*p->u.array.u.values);
+ s->fast_array_elements += p->u.array.count;
+ for (i = 0; i < p->u.array.count; i++) {
+ compute_value_size(p->u.array.u.values[i], hp);
+ }
+ }
+ }
+ break;
+ case JS_CLASS_NUMBER: /* u.object_data */
+ case JS_CLASS_STRING: /* u.object_data */
+ case JS_CLASS_BOOLEAN: /* u.object_data */
+ case JS_CLASS_SYMBOL: /* u.object_data */
+ case JS_CLASS_DATE: /* u.object_data */
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT: /* u.object_data */
+ case JS_CLASS_BIG_FLOAT: /* u.object_data */
+ case JS_CLASS_BIG_DECIMAL: /* u.object_data */
+#endif
+ compute_value_size(p->u.object_data, hp);
+ break;
+ case JS_CLASS_C_FUNCTION: /* u.cfunc */
+ s->c_func_count++;
+ break;
+ case JS_CLASS_BYTECODE_FUNCTION: /* u.func */
+ {
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ JSVarRef **var_refs = p->u.func.var_refs;
+ /* home_object: object will be accounted for in list scan */
+ if (var_refs) {
+ s->memory_used_count++;
+ s->js_func_size += b->closure_var_count * sizeof(*var_refs);
+ for (i = 0; i < b->closure_var_count; i++) {
+ if (var_refs[i]) {
+ double ref_count = var_refs[i]->header.ref_count;
+ s->memory_used_count += 1 / ref_count;
+ s->js_func_size += sizeof(*var_refs[i]) / ref_count;
+ /* handle non object closed values */
+ if (var_refs[i]->pvalue == &var_refs[i]->value) {
+ /* potential multiple count */
+ compute_value_size(var_refs[i]->value, hp);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */
+ {
+ JSBoundFunction *bf = p->u.bound_function;
+ /* func_obj and this_val are objects */
+ for (i = 0; i < bf->argc; i++) {
+ compute_value_size(bf->argv[i], hp);
+ }
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);
+ }
+ break;
+ case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */
+ {
+ JSCFunctionDataRecord *fd = p->u.c_function_data_record;
+ if (fd) {
+ for (i = 0; i < fd->data_len; i++) {
+ compute_value_size(fd->data[i], hp);
+ }
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);
+ }
+ }
+ break;
+ case JS_CLASS_REGEXP: /* u.regexp */
+ compute_jsstring_size(p->u.regexp.pattern, hp);
+ compute_jsstring_size(p->u.regexp.bytecode, hp);
+ break;
+
+ case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */
+ {
+ JSForInIterator *it = p->u.for_in_iterator;
+ if (it) {
+ compute_value_size(it->obj, hp);
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*it);
+ }
+ }
+ break;
+ case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */
+ case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */
+ {
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ if (abuf) {
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*abuf);
+ if (abuf->data) {
+ s->memory_used_count += 1;
+ s->memory_used_size += abuf->byte_length;
+ }
+ }
+ }
+ break;
+ case JS_CLASS_GENERATOR: /* u.generator_data */
+ case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */
+#endif
+ case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_DATAVIEW: /* u.typed_array */
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_FLOAT_ENV: /* u.float_env */
+#endif
+ case JS_CLASS_MAP: /* u.map_state */
+ case JS_CLASS_SET: /* u.map_state */
+ case JS_CLASS_WEAKMAP: /* u.map_state */
+ case JS_CLASS_WEAKSET: /* u.map_state */
+ case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */
+ case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */
+ case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */
+ case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */
+ case JS_CLASS_PROXY: /* u.proxy_data */
+ case JS_CLASS_PROMISE: /* u.promise_data */
+ case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */
+ case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */
+ case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */
+ case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */
+ case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */
+ case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */
+ /* TODO */
+ default:
+ /* XXX: class definition should have an opaque block size */
+ if (p->u.opaque) {
+ s->memory_used_count += 1;
+ }
+ break;
+ }
+ }
+ s->obj_size += s->obj_count * sizeof(JSObject);
+
+ /* hashed shapes */
+ s->memory_used_count++; /* rt->shape_hash */
+ s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;
+ for(i = 0; i < rt->shape_hash_size; i++) {
+ JSShape *sh;
+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
+ int hash_size = sh->prop_hash_mask + 1;
+ s->shape_count++;
+ s->shape_size += get_shape_size(hash_size, sh->prop_size);
+ }
+ }
+
+ /* atoms */
+ s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */
+ s->atom_count = rt->atom_count;
+ s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +
+ sizeof(rt->atom_hash[0]) * rt->atom_hash_size;
+ for(i = 0; i < rt->atom_size; i++) {
+ JSAtomStruct *p = rt->atom_array[i];
+ if (!atom_is_free(p)) {
+ s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +
+ 1 - p->is_wide_char);
+ }
+ }
+ s->str_count = round(mem.str_count);
+ s->str_size = round(mem.str_size);
+ s->js_func_count = mem.js_func_count;
+ s->js_func_size = round(mem.js_func_size);
+ s->js_func_code_size = mem.js_func_code_size;
+ s->js_func_pc2line_count = mem.js_func_pc2line_count;
+ s->js_func_pc2line_size = mem.js_func_pc2line_size;
+ s->memory_used_count += round(mem.memory_used_count) +
+ s->atom_count + s->str_count +
+ s->obj_count + s->shape_count +
+ s->js_func_count + s->js_func_pc2line_count;
+ s->memory_used_size += s->atom_size + s->str_size +
+ s->obj_size + s->prop_size + s->shape_size +
+ s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
+}
+
+void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
+{
+ fprintf(fp, "QuickJS memory usage -- "
+#ifdef CONFIG_BIGNUM
+ "BigNum "
+#endif
+ CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
+ (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit);
+#if 1
+ if (rt) {
+ static const struct {
+ const char *name;
+ size_t size;
+ } object_types[] = {
+ { "JSRuntime", sizeof(JSRuntime) },
+ { "JSContext", sizeof(JSContext) },
+ { "JSObject", sizeof(JSObject) },
+ { "JSString", sizeof(JSString) },
+ { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
+ };
+ int i, usage_size_ok = 0;
+ for(i = 0; i < countof(object_types); i++) {
+ unsigned int size = object_types[i].size;
+ void *p = js_malloc_rt(rt, size);
+ if (p) {
+ unsigned int size1 = js_malloc_usable_size_rt(rt, p);
+ if (size1 >= size) {
+ usage_size_ok = 1;
+ fprintf(fp, " %3u + %-2u %s\n",
+ size, size1 - size, object_types[i].name);
+ }
+ js_free_rt(rt, p);
+ }
+ }
+ if (!usage_size_ok) {
+ fprintf(fp, " malloc_usable_size unavailable\n");
+ }
+ {
+ int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
+ int class_id;
+ struct list_head *el;
+ list_for_each(el, &rt->gc_obj_list) {
+ JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
+ JSObject *p;
+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ p = (JSObject *)gp;
+ obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
+ }
+ }
+ fprintf(fp, "\n" "JSObject classes\n");
+ if (obj_classes[0])
+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");
+ for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
+ if (obj_classes[class_id]) {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,
+ JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));
+ }
+ }
+ if (obj_classes[JS_CLASS_INIT_COUNT])
+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
+ }
+ fprintf(fp, "\n");
+ }
+#endif
+ fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
+
+ if (s->malloc_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",
+ "memory allocated", s->malloc_count, s->malloc_size,
+ (double)s->malloc_size / s->malloc_count);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",
+ "memory used", s->memory_used_count, s->memory_used_size,
+ MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
+ s->memory_used_count));
+ }
+ if (s->atom_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",
+ "atoms", s->atom_count, s->atom_size,
+ (double)s->atom_size / s->atom_count);
+ }
+ if (s->str_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",
+ "strings", s->str_count, s->str_size,
+ (double)s->str_size / s->str_count);
+ }
+ if (s->obj_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
+ "objects", s->obj_count, s->obj_size,
+ (double)s->obj_size / s->obj_count);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
+ " properties", s->prop_count, s->prop_size,
+ (double)s->prop_count / s->obj_count);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",
+ " shapes", s->shape_count, s->shape_size,
+ (double)s->shape_size / s->shape_count);
+ }
+ if (s->js_func_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
+ "bytecode functions", s->js_func_count, s->js_func_size);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
+ " bytecode", s->js_func_count, s->js_func_code_size,
+ (double)s->js_func_code_size / s->js_func_count);
+ if (s->js_func_pc2line_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
+ " pc2line", s->js_func_pc2line_count,
+ s->js_func_pc2line_size,
+ (double)s->js_func_pc2line_size / s->js_func_pc2line_count);
+ }
+ }
+ if (s->c_func_count) {
+ fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
+ }
+ if (s->array_count) {
+ fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
+ if (s->fast_array_count) {
+ fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",
+ " elements", s->fast_array_elements,
+ s->fast_array_elements * (int)sizeof(JSValue),
+ (double)s->fast_array_elements / s->fast_array_count);
+ }
+ }
+ if (s->binary_object_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
+ "binary objects", s->binary_object_count, s->binary_object_size);
+ }
+}
+
+JSValue JS_GetGlobalObject(JSContext *ctx)
+{
+ return JS_DupValue(ctx, ctx->global_obj);
+}
+
+/* WARNING: obj is freed */
+JSValue JS_Throw(JSContext *ctx, JSValue obj)
+{
+ JSRuntime *rt = ctx->rt;
+ JS_FreeValue(ctx, rt->current_exception);
+ rt->current_exception = obj;
+ return JS_EXCEPTION;
+}
+
+/* return the pending exception (cannot be called twice). */
+JSValue JS_GetException(JSContext *ctx)
+{
+ JSValue val;
+ JSRuntime *rt = ctx->rt;
+ val = rt->current_exception;
+ rt->current_exception = JS_NULL;
+ return val;
+}
+
+static void dbuf_put_leb128(DynBuf *s, uint32_t v)
+{
+ uint32_t a;
+ for(;;) {
+ a = v & 0x7f;
+ v >>= 7;
+ if (v != 0) {
+ dbuf_putc(s, a | 0x80);
+ } else {
+ dbuf_putc(s, a);
+ break;
+ }
+ }
+}
+
+static void dbuf_put_sleb128(DynBuf *s, int32_t v1)
+{
+ uint32_t v = v1;
+ dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));
+}
+
+static int get_leb128(uint32_t *pval, const uint8_t *buf,
+ const uint8_t *buf_end)
+{
+ const uint8_t *ptr = buf;
+ uint32_t v, a, i;
+ v = 0;
+ for(i = 0; i < 5; i++) {
+ if (unlikely(ptr >= buf_end))
+ break;
+ a = *ptr++;
+ v |= (a & 0x7f) << (i * 7);
+ if (!(a & 0x80)) {
+ *pval = v;
+ return ptr - buf;
+ }
+ }
+ *pval = 0;
+ return -1;
+}
+
+static int get_sleb128(int32_t *pval, const uint8_t *buf,
+ const uint8_t *buf_end)
+{
+ int ret;
+ uint32_t val;
+ ret = get_leb128(&val, buf, buf_end);
+ if (ret < 0) {
+ *pval = 0;
+ return -1;
+ }
+ *pval = (val >> 1) ^ -(val & 1);
+ return ret;
+}
+
+static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,
+ uint32_t pc_value)
+{
+ const uint8_t *p_end, *p;
+ int new_line_num, line_num, pc, v, ret;
+ unsigned int op;
+
+ if (!b->has_debug || !b->debug.pc2line_buf) {
+ /* function was stripped */
+ return -1;
+ }
+
+ p = b->debug.pc2line_buf;
+ p_end = p + b->debug.pc2line_len;
+ pc = 0;
+ line_num = b->debug.line_num;
+ while (p < p_end) {
+ op = *p++;
+ if (op == 0) {
+ uint32_t val;
+ ret = get_leb128(&val, p, p_end);
+ if (ret < 0)
+ goto fail;
+ pc += val;
+ p += ret;
+ ret = get_sleb128(&v, p, p_end);
+ if (ret < 0) {
+ fail:
+ /* should never happen */
+ return b->debug.line_num;
+ }
+ p += ret;
+ new_line_num = line_num + v;
+ } else {
+ op -= PC2LINE_OP_FIRST;
+ pc += (op / PC2LINE_RANGE);
+ new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
+ }
+ if (pc_value < pc)
+ return line_num;
+ line_num = new_line_num;
+ }
+ return line_num;
+}
+
+/* in order to avoid executing arbitrary code during the stack trace
+ generation, we only look at simple 'name' properties containing a
+ string. */
+static const char *get_func_name(JSContext *ctx, JSValueConst func)
+{
+ JSProperty *pr;
+ JSShapeProperty *prs;
+ JSValueConst val;
+
+ if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)
+ return NULL;
+ prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);
+ if (!prs)
+ return NULL;
+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
+ return NULL;
+ val = pr->u.value;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
+ return NULL;
+ return JS_ToCString(ctx, val);
+}
+
+#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
+/* only taken into account if filename is provided */
+#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1)
+
+/* if filename != NULL, an additional level is added with the filename
+ and line number information (used for parse error). */
+void build_backtrace(JSContext *ctx, JSValueConst error_obj,
+ const char *filename, int line_num,
+ int backtrace_flags)
+{
+ JSStackFrame *sf;
+ JSValue str;
+ DynBuf dbuf;
+ const char *func_name_str;
+ const char *str1;
+ JSObject *p;
+ BOOL backtrace_barrier;
+
+ js_dbuf_init(ctx, &dbuf);
+ if (filename) {
+ dbuf_printf(&dbuf, " at %s", filename);
+ if (line_num != -1)
+ dbuf_printf(&dbuf, ":%d", line_num);
+ dbuf_putc(&dbuf, '\n');
+ str = JS_NewString(ctx, filename);
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
+ goto done;
+ }
+ for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
+ if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
+ backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
+ continue;
+ }
+ func_name_str = get_func_name(ctx, sf->cur_func);
+ if (!func_name_str || func_name_str[0] == '\0')
+ str1 = "<anonymous>";
+ else
+ str1 = func_name_str;
+ dbuf_printf(&dbuf, " at %s", str1);
+ JS_FreeCString(ctx, func_name_str);
+
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ backtrace_barrier = FALSE;
+ if (js_class_has_bytecode(p->class_id)) {
+ JSFunctionBytecode *b;
+ const char *atom_str;
+ int line_num1;
+
+ b = p->u.func.function_bytecode;
+ backtrace_barrier = b->backtrace_barrier;
+ if (b->has_debug) {
+ line_num1 = find_line_num(ctx, b,
+ sf->cur_pc - b->byte_code_buf - 1);
+ atom_str = JS_AtomToCString(ctx, b->debug.filename);
+ dbuf_printf(&dbuf, " (%s",
+ atom_str ? atom_str : "<null>");
+ JS_FreeCString(ctx, atom_str);
+ if (line_num1 != -1)
+ dbuf_printf(&dbuf, ":%d", line_num1);
+ dbuf_putc(&dbuf, ')');
+ }
+ } else {
+ dbuf_printf(&dbuf, " (native)");
+ }
+ dbuf_putc(&dbuf, '\n');
+ /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
+ if (backtrace_barrier)
+ break;
+ }
+ done:
+ dbuf_putc(&dbuf, '\0');
+ if (dbuf_error(&dbuf))
+ str = JS_NULL;
+ else
+ str = JS_NewString(ctx, (char *)dbuf.buf);
+ dbuf_free(&dbuf);
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+}
+
+/* Note: it is important that no exception is returned by this function */
+static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id != JS_CLASS_ERROR)
+ return FALSE;
+ if (find_own_property1(p, JS_ATOM_stack))
+ return FALSE;
+ return TRUE;
+}
+
+JSValue JS_NewError(JSContext *ctx)
+{
+ return JS_NewObjectClass(ctx, JS_CLASS_ERROR);
+}
+
+static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
+ const char *fmt, va_list ap, BOOL add_backtrace)
+{
+ char buf[8192];
+ JSValue obj, ret;
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],
+ JS_CLASS_ERROR);
+ if (unlikely(JS_IsException(obj))) {
+ /* out of memory: throw JS_NULL to avoid recursing */
+ obj = JS_NULL;
+ } else {
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,
+ JS_NewString(ctx, buf),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+ if (add_backtrace) {
+ build_backtrace(ctx, obj, NULL, 0, 0);
+ }
+ ret = JS_Throw(ctx, obj);
+ return ret;
+}
+
+static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
+ const char *fmt, va_list ap)
+{
+ JSRuntime *rt = ctx->rt;
+ JSStackFrame *sf;
+ BOOL add_backtrace;
+
+ /* the backtrace is added later if called from a bytecode function */
+ sf = rt->current_stack_frame;
+ add_backtrace = !rt->in_out_of_memory &&
+ (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));
+ return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+static int FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
+{
+ va_list ap;
+
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ va_start(ap, fmt);
+ JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
+ va_end(ap);
+ return -1;
+ } else {
+ return FALSE;
+ }
+}
+
+/* never use it directly */
+static JSValue FORMAT_ATTR(3, 4) JS_ThrowTypeErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowTypeError(ctx, fmt,
+ JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
+}
+
+/* never use it directly */
+static JSValue FORMAT_ATTR(3, 4) JS_ThrowSyntaxErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowSyntaxError(ctx, fmt,
+ JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
+}
+
+/* %s is replaced by 'atom'. The macro is used so that gcc can check
+ the format string. */
+#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) JS_ThrowTypeErrorAtomImpl(ctx, atom, fmt, "")
+#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) JS_ThrowSyntaxErrorAtomImpl(ctx, atom, fmt, "")
+
+static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
+{
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
+ return -1;
+ } else {
+ return FALSE;
+ }
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+JSValue JS_ThrowOutOfMemory(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ if (!rt->in_out_of_memory) {
+ rt->in_out_of_memory = TRUE;
+ JS_ThrowInternalError(ctx, "out of memory");
+ rt->in_out_of_memory = FALSE;
+ }
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ThrowStackOverflow(JSContext *ctx)
+{
+ return JS_ThrowInternalError(ctx, "stack overflow");
+}
+
+static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "not an object");
+}
+
+static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "not a symbol");
+}
+
+static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowReferenceError(ctx, "'%s' is not defined",
+ JS_AtomGetStr(ctx, buf, sizeof(buf), name));
+}
+
+static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowReferenceError(ctx, "%s is not initialized",
+ name == JS_ATOM_NULL ? "lexical variable" :
+ JS_AtomGetStr(ctx, buf, sizeof(buf), name));
+}
+
+static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,
+ JSFunctionBytecode *b,
+ int idx, BOOL is_ref)
+{
+ JSAtom atom = JS_ATOM_NULL;
+ if (is_ref) {
+ atom = b->closure_var[idx].var_name;
+ } else {
+ /* not present if the function is stripped and contains no eval() */
+ if (b->vardefs)
+ atom = b->vardefs[b->arg_count + idx].var_name;
+ }
+ return JS_ThrowReferenceErrorUninitialized(ctx, atom);
+}
+
+static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
+{
+ JSRuntime *rt = ctx->rt;
+ JSAtom name;
+ name = rt->class_array[class_id].class_name;
+ return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
+}
+
+static no_inline warn_unused int js_poll_interrupts_impl(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
+ if (rt->interrupt_handler) {
+ if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
+ /* XXX: should set a specific flag to avoid catching */
+ JS_ThrowInternalError(ctx, "interrupted");
+ JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static inline warn_unused int js_poll_interrupts(JSContext *ctx)
+{
+ if (unlikely(--ctx->interrupt_counter <= 0)) {
+ return js_poll_interrupts_impl(ctx);
+ } else {
+ return 0;
+ }
+}
+
+/* return -1 (exception) or TRUE/FALSE */
+static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
+ JSValueConst proto_val,
+ BOOL throw_flag)
+{
+ JSObject *proto, *p, *p1;
+ JSShape *sh;
+
+ if (throw_flag) {
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||
+ JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)
+ goto not_obj;
+ } else {
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ goto not_obj;
+ }
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {
+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {
+ not_obj:
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ proto = NULL;
+ } else {
+ proto = JS_VALUE_GET_OBJ(proto_val);
+ }
+
+ if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return TRUE;
+
+ if (unlikely(p->class_id == JS_CLASS_PROXY))
+ return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);
+ sh = p->shape;
+ if (sh->proto == proto)
+ return TRUE;
+ if (!p->extensible) {
+ if (throw_flag) {
+ JS_ThrowTypeError(ctx, "object is not extensible");
+ return -1;
+ } else {
+ return FALSE;
+ }
+ }
+ if (proto) {
+ /* check if there is a cycle */
+ p1 = proto;
+ do {
+ if (p1 == p) {
+ if (throw_flag) {
+ JS_ThrowTypeError(ctx, "circular prototype chain");
+ return -1;
+ } else {
+ return FALSE;
+ }
+ }
+ /* Note: for Proxy objects, proto is NULL */
+ p1 = p1->shape->proto;
+ } while (p1 != NULL);
+ JS_DupValue(ctx, proto_val);
+ }
+
+ if (js_shape_prepare_update(ctx, p, NULL))
+ return -1;
+ sh = p->shape;
+ if (sh->proto)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
+ sh->proto = proto;
+ return TRUE;
+}
+
+/* return -1 (exception) or TRUE/FALSE */
+int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
+{
+ return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);
+}
+
+/* Only works for primitive types, otherwise return JS_NULL. */
+static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
+{
+ switch(JS_VALUE_GET_NORM_TAG(val)) {
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ val = ctx->class_proto[JS_CLASS_BIG_INT];
+ break;
+ case JS_TAG_BIG_FLOAT:
+ val = ctx->class_proto[JS_CLASS_BIG_FLOAT];
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];
+ break;
+#endif
+ case JS_TAG_INT:
+ case JS_TAG_FLOAT64:
+ val = ctx->class_proto[JS_CLASS_NUMBER];
+ break;
+ case JS_TAG_BOOL:
+ val = ctx->class_proto[JS_CLASS_BOOLEAN];
+ break;
+ case JS_TAG_STRING:
+ val = ctx->class_proto[JS_CLASS_STRING];
+ break;
+ case JS_TAG_SYMBOL:
+ val = ctx->class_proto[JS_CLASS_SYMBOL];
+ break;
+ case JS_TAG_OBJECT:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ default:
+ val = JS_NULL;
+ break;
+ }
+ return val;
+}
+
+/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */
+JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
+{
+ JSValue val;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (unlikely(p->class_id == JS_CLASS_PROXY)) {
+ val = js_proxy_getPrototypeOf(ctx, obj);
+ } else {
+ p = p->shape->proto;
+ if (!p)
+ val = JS_NULL;
+ else
+ val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ }
+ } else {
+ val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
+ }
+ return val;
+}
+
+static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)
+{
+ JSValue obj1;
+ obj1 = JS_GetPrototype(ctx, obj);
+ JS_FreeValue(ctx, obj);
+ return obj1;
+}
+
+/* return TRUE, FALSE or (-1) in case of exception */
+static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
+ JSValueConst obj)
+{
+ JSValue obj_proto;
+ JSObject *proto;
+ const JSObject *p, *proto1;
+ BOOL ret;
+
+ if (!JS_IsFunction(ctx, obj))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id == JS_CLASS_BOUND_FUNCTION) {
+ JSBoundFunction *s = p->u.bound_function;
+ return JS_IsInstanceOf(ctx, val, s->func_obj);
+ }
+
+ /* Only explicitly boxed values are instances of constructors */
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);
+ if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {
+ if (!JS_IsException(obj_proto))
+ JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");
+ ret = -1;
+ goto done;
+ }
+ proto = JS_VALUE_GET_OBJ(obj_proto);
+ p = JS_VALUE_GET_OBJ(val);
+ for(;;) {
+ proto1 = p->shape->proto;
+ if (!proto1) {
+ /* slow case if proxy in the prototype chain */
+ if (unlikely(p->class_id == JS_CLASS_PROXY)) {
+ JSValue obj1;
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
+ for(;;) {
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsException(obj1)) {
+ ret = -1;
+ break;
+ }
+ if (JS_IsNull(obj1)) {
+ ret = FALSE;
+ break;
+ }
+ if (proto == JS_VALUE_GET_OBJ(obj1)) {
+ JS_FreeValue(ctx, obj1);
+ ret = TRUE;
+ break;
+ }
+ /* must check for timeout to avoid infinite loop */
+ if (js_poll_interrupts(ctx)) {
+ JS_FreeValue(ctx, obj1);
+ ret = -1;
+ break;
+ }
+ }
+ } else {
+ ret = FALSE;
+ }
+ break;
+ }
+ p = proto1;
+ if (proto == p) {
+ ret = TRUE;
+ break;
+ }
+ }
+done:
+ JS_FreeValue(ctx, obj_proto);
+ return ret;
+}
+
+/* return TRUE, FALSE or (-1) in case of exception */
+int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)
+{
+ JSValue method;
+
+ if (!JS_IsObject(obj))
+ goto fail;
+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);
+ if (JS_IsException(method))
+ return -1;
+ if (!JS_IsNull(method) && !JS_IsUndefined(method)) {
+ JSValue ret;
+ ret = JS_CallFree(ctx, method, obj, 1, &val);
+ return JS_ToBoolFree(ctx, ret);
+ }
+
+ /* legacy case */
+ if (!JS_IsFunction(ctx, obj)) {
+ fail:
+ JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");
+ return -1;
+ }
+ return JS_OrdinaryIsInstanceOf(ctx, val, obj);
+}
+
+/* return the value associated to the autoinit property or an exception */
+typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
+
+static JSAutoInitFunc *js_autoinit_func_table[] = {
+ js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */
+ js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */
+ JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */
+};
+
+/* warning: 'prs' is reallocated after it */
+static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
+ JSProperty *pr, JSShapeProperty *prs)
+{
+ JSValue val;
+ JSContext *realm;
+ JSAutoInitFunc *func;
+
+ if (js_shape_prepare_update(ctx, p, &prs))
+ return -1;
+
+ realm = js_autoinit_get_realm(pr);
+ func = js_autoinit_func_table[js_autoinit_get_id(pr)];
+ /* 'func' shall not modify the object properties 'pr' */
+ val = func(realm, p, prop, pr->u.init.opaque);
+ js_autoinit_free(ctx->rt, pr);
+ prs->flags &= ~JS_PROP_TMASK;
+ pr->u.value = JS_UNDEFINED;
+ if (JS_IsException(val))
+ return -1;
+ pr->u.value = val;
+ return 0;
+}
+
+JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst this_obj,
+ BOOL throw_ref_error)
+{
+ JSObject *p;
+ JSProperty *pr;
+ JSShapeProperty *prs;
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_TAG(obj);
+ if (unlikely(tag != JS_TAG_OBJECT)) {
+ switch(tag) {
+ case JS_TAG_NULL:
+ return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);
+ case JS_TAG_UNDEFINED:
+ return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);
+ case JS_TAG_EXCEPTION:
+ return JS_EXCEPTION;
+ case JS_TAG_STRING:
+ {
+ JSString *p1 = JS_VALUE_GET_STRING(obj);
+ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx, ch;
+ idx = JS_AtomToUInt32(prop);
+ if (idx < p1->len) {
+ if (p1->is_wide_char)
+ ch = p1->u.str16[idx];
+ else
+ ch = p1->u.str8[idx];
+ return js_new_string_char(ctx, ch);
+ }
+ } else if (prop == JS_ATOM_length) {
+ return JS_NewInt32(ctx, p1->len);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ /* cannot raise an exception */
+ p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
+ if (!p)
+ return JS_UNDEFINED;
+ } else {
+ p = JS_VALUE_GET_OBJ(obj);
+ }
+
+ for(;;) {
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* found */
+ if (unlikely(prs->flags & JS_PROP_TMASK)) {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ if (unlikely(!pr->u.getset.getter)) {
+ return JS_UNDEFINED;
+ } else {
+ JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);
+ /* Note: the field could be removed in the getter */
+ func = JS_DupValue(ctx, func);
+ return JS_CallFree(ctx, func, this_obj, 0, NULL);
+ }
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ JSValue val = *pr->u.var_ref->pvalue;
+ if (unlikely(JS_IsUninitialized(val)))
+ return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return JS_DupValue(ctx, val);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry */
+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
+ return JS_EXCEPTION;
+ continue;
+ }
+ } else {
+ if (JS_IsUndefined(pr->u.value) && ctx->handleUndefined)
+ ctx->handleUndefined(ctx);
+ return JS_DupValue(ctx, pr->u.value);
+ }
+ }
+ if (unlikely(p->is_exotic)) {
+ /* exotic behaviors */
+ if (p->fast_array) {
+ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ /* we avoid duplicating the code */
+ return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ return JS_UNDEFINED;
+ }
+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ int ret;
+ ret = JS_AtomIsNumericIndex(ctx, prop);
+ if (ret != 0) {
+ if (ret < 0)
+ return JS_EXCEPTION;
+ return JS_UNDEFINED;
+ }
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em) {
+ if (em->get_property) {
+ JSValue obj1, retval;
+ /* XXX: should pass throw_ref_error */
+ /* Note: if 'p' is a prototype, it can be
+ freed in the called function */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ retval = em->get_property(ctx, obj1, prop, this_obj);
+ JS_FreeValue(ctx, obj1);
+ return retval;
+ }
+ if (em->get_own_property) {
+ JSPropertyDescriptor desc;
+ int ret;
+ JSValue obj1;
+
+ /* Note: if 'p' is a prototype, it can be
+ freed in the called function */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = em->get_own_property(ctx, &desc, obj1, prop);
+ JS_FreeValue(ctx, obj1);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JS_FreeValue(ctx, desc.setter);
+ return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);
+ } else {
+ return desc.value;
+ }
+ }
+ }
+ }
+ }
+ }
+ p = p->shape->proto;
+ if (!p)
+ break;
+ }
+ if (unlikely(throw_ref_error)) {
+ return JS_ThrowReferenceErrorNotDefined(ctx, prop);
+ } else {
+ return JS_UNDEFINED;
+ }
+}
+
+static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
+{
+ return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
+ atom);
+}
+
+/* Private fields can be added even on non extensible objects or
+ Proxies */
+static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
+ JSValueConst name, JSValue val)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSAtom prop;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto fail;
+ }
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
+ JS_ThrowTypeErrorNotASymbol(ctx);
+ goto fail;
+ }
+ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
+ prop);
+ goto fail;
+ }
+ pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
+ if (unlikely(!pr)) {
+ fail:
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ pr->u.value = val;
+ return 0;
+}
+
+static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
+ JSValueConst name)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSAtom prop;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
+ return JS_ThrowTypeErrorNotASymbol(ctx);
+ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (!prs) {
+ JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
+ return JS_EXCEPTION;
+ }
+ return JS_DupValue(ctx, pr->u.value);
+}
+
+static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
+ JSValueConst name, JSValue val)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSAtom prop;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto fail;
+ }
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
+ JS_ThrowTypeErrorNotASymbol(ctx);
+ goto fail;
+ }
+ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (!prs) {
+ JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
+ fail:
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ set_value(ctx, &pr->u.value, val);
+ return 0;
+}
+
+static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
+{
+ JSObject *p, *p1;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSValue brand;
+ JSAtom brand_atom;
+
+ if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ p = JS_VALUE_GET_OBJ(home_obj);
+ prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
+ if (!prs) {
+ brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
+ if (JS_IsException(brand))
+ return -1;
+ /* if the brand is not present, add it */
+ pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
+ if (!pr) {
+ JS_FreeValue(ctx, brand);
+ return -1;
+ }
+ pr->u.value = JS_DupValue(ctx, brand);
+ } else {
+ brand = JS_DupValue(ctx, pr->u.value);
+ }
+ brand_atom = js_symbol_to_atom(ctx, brand);
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ JS_FreeAtom(ctx, brand_atom);
+ return -1;
+ }
+ p1 = JS_VALUE_GET_OBJ(obj);
+ pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, brand_atom);
+ if (!pr)
+ return -1;
+ pr->u.value = JS_UNDEFINED;
+ return 0;
+}
+
+static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
+{
+ JSObject *p, *p1, *home_obj;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSValueConst brand;
+
+ /* get the home object of 'func' */
+ if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {
+ not_obj:
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ p1 = JS_VALUE_GET_OBJ(func);
+ if (!js_class_has_bytecode(p1->class_id))
+ goto not_obj;
+ home_obj = p1->u.func.home_object;
+ if (!home_obj)
+ goto not_obj;
+ prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
+ if (!prs) {
+ JS_ThrowTypeError(ctx, "expecting <brand> private field");
+ return -1;
+ }
+ brand = pr->u.value;
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))
+ goto not_obj;
+
+ /* get the brand array of 'obj' */
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ goto not_obj;
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand));
+ if (!prs) {
+ JS_ThrowTypeError(ctx, "invalid brand on object");
+ return -1;
+ }
+ return 0;
+}
+
+static uint32_t js_string_obj_get_length(JSContext *ctx,
+ JSValueConst obj)
+{
+ JSObject *p;
+ JSString *p1;
+ uint32_t len = 0;
+
+ /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
+ p1 = JS_VALUE_GET_STRING(p->u.object_data);
+ len = p1->len;
+ }
+ return len;
+}
+
+static int num_keys_cmp(const void *p1, const void *p2, void *opaque)
+{
+ JSContext *ctx = opaque;
+ JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;
+ JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;
+ uint32_t v1, v2;
+ BOOL atom1_is_integer, atom2_is_integer;
+
+ atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);
+ atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);
+ assert(atom1_is_integer && atom2_is_integer);
+ if (v1 < v2)
+ return -1;
+ else if (v1 == v2)
+ return 0;
+ else
+ return 1;
+}
+
+static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
+{
+ uint32_t i;
+ if (tab) {
+ for(i = 0; i < len; i++)
+ JS_FreeAtom(ctx, tab[i].atom);
+ js_free(ctx, tab);
+ }
+}
+
+/* return < 0 in case if exception, 0 if OK. ptab and its atoms must
+ be freed by the user. */
+static int warn_unused JS_GetOwnPropertyNamesInternal(JSContext *ctx,
+ JSPropertyEnum **ptab,
+ uint32_t *plen,
+ JSObject *p, int flags)
+{
+ int i, j;
+ JSShape *sh;
+ JSShapeProperty *prs;
+ JSPropertyEnum *tab_atom, *tab_exotic;
+ JSAtom atom;
+ uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;
+ uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count;
+ BOOL is_enumerable, num_sorted;
+ uint32_t num_key;
+ JSAtomKindEnum kind;
+
+ /* clear pointer for consistency in case of failure */
+ *ptab = NULL;
+ *plen = 0;
+
+ /* compute the number of returned properties */
+ num_keys_count = 0;
+ str_keys_count = 0;
+ sym_keys_count = 0;
+ exotic_keys_count = 0;
+ exotic_count = 0;
+ tab_exotic = NULL;
+ sh = p->shape;
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ atom = prs->atom;
+ if (atom != JS_ATOM_NULL) {
+ is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
+ kind = JS_AtomGetKind(ctx, atom);
+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
+ ((flags >> kind) & 1) != 0) {
+ /* need to raise an exception in case of the module
+ name space (implicit GetOwnProperty) */
+ if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&
+ (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {
+ JSVarRef *var_ref = p->prop[i].u.var_ref;
+ if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ }
+ if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
+ num_keys_count++;
+ } else if (kind == JS_ATOM_KIND_STRING) {
+ str_keys_count++;
+ } else {
+ sym_keys_count++;
+ }
+ }
+ }
+ }
+
+ if (p->is_exotic) {
+ if (p->fast_array) {
+ if (flags & JS_GPN_STRING_MASK) {
+ num_keys_count += p->u.array.count;
+ }
+ } else if (p->class_id == JS_CLASS_STRING) {
+ if (flags & JS_GPN_STRING_MASK) {
+ num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->get_own_property_names) {
+ if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,
+ JS_MKPTR(JS_TAG_OBJECT, p)))
+ return -1;
+ for(i = 0; i < exotic_count; i++) {
+ atom = tab_exotic[i].atom;
+ kind = JS_AtomGetKind(ctx, atom);
+ if (((flags >> kind) & 1) != 0) {
+ is_enumerable = FALSE;
+ if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {
+ JSPropertyDescriptor desc;
+ int res;
+ /* set the "is_enumerable" field if necessary */
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
+ if (res < 0) {
+ js_free_prop_enum(ctx, tab_exotic, exotic_count);
+ return -1;
+ }
+ if (res) {
+ is_enumerable =
+ ((desc.flags & JS_PROP_ENUMERABLE) != 0);
+ js_free_desc(ctx, &desc);
+ }
+ tab_exotic[i].is_enumerable = is_enumerable;
+ }
+ if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {
+ exotic_keys_count++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* fill them */
+
+ atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count;
+ /* avoid allocating 0 bytes */
+ tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));
+ if (!tab_atom) {
+ js_free_prop_enum(ctx, tab_exotic, exotic_count);
+ return -1;
+ }
+
+ num_index = 0;
+ str_index = num_keys_count;
+ sym_index = str_index + str_keys_count;
+
+ num_sorted = TRUE;
+ sh = p->shape;
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ atom = prs->atom;
+ if (atom != JS_ATOM_NULL) {
+ is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
+ kind = JS_AtomGetKind(ctx, atom);
+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
+ ((flags >> kind) & 1) != 0) {
+ if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
+ j = num_index++;
+ num_sorted = FALSE;
+ } else if (kind == JS_ATOM_KIND_STRING) {
+ j = str_index++;
+ } else {
+ j = sym_index++;
+ }
+ tab_atom[j].atom = JS_DupAtom(ctx, atom);
+ tab_atom[j].is_enumerable = is_enumerable;
+ }
+ }
+ }
+
+ if (p->is_exotic) {
+ int len;
+ if (p->fast_array) {
+ if (flags & JS_GPN_STRING_MASK) {
+ len = p->u.array.count;
+ goto add_array_keys;
+ }
+ } else if (p->class_id == JS_CLASS_STRING) {
+ if (flags & JS_GPN_STRING_MASK) {
+ len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ add_array_keys:
+ for(i = 0; i < len; i++) {
+ tab_atom[num_index].atom = JS_AtomFromUInt32(i);
+ if (tab_atom[num_index].atom == JS_ATOM_NULL) {
+ js_free_prop_enum(ctx, tab_atom, num_index);
+ return -1;
+ }
+ tab_atom[num_index].is_enumerable = TRUE;
+ num_index++;
+ }
+ }
+ } else {
+ /* Note: exotic keys are not reordered and comes after the object own properties. */
+ for(i = 0; i < exotic_count; i++) {
+ atom = tab_exotic[i].atom;
+ is_enumerable = tab_exotic[i].is_enumerable;
+ kind = JS_AtomGetKind(ctx, atom);
+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
+ ((flags >> kind) & 1) != 0) {
+ tab_atom[sym_index].atom = atom;
+ tab_atom[sym_index].is_enumerable = is_enumerable;
+ sym_index++;
+ } else {
+ JS_FreeAtom(ctx, atom);
+ }
+ }
+ js_free(ctx, tab_exotic);
+ }
+ }
+
+ assert(num_index == num_keys_count);
+ assert(str_index == num_keys_count + str_keys_count);
+ assert(sym_index == atom_count);
+
+ if (num_keys_count != 0 && !num_sorted) {
+ rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,
+ ctx);
+ }
+ *ptab = tab_atom;
+ *plen = atom_count;
+ return 0;
+}
+
+int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
+ uint32_t *plen, JSValueConst obj, int flags)
+{
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
+ JS_VALUE_GET_OBJ(obj), flags);
+}
+
+/* Return -1 if exception,
+ FALSE if the property does not exist, TRUE if it exists. If TRUE is
+ returned, the property descriptor 'desc' is filled present. */
+static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSObject *p, JSAtom prop)
+{
+ JSShapeProperty *prs;
+ JSProperty *pr;
+
+retry:
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ if (desc) {
+ desc->flags = prs->flags & JS_PROP_C_W_E;
+ desc->getter = JS_UNDEFINED;
+ desc->setter = JS_UNDEFINED;
+ desc->value = JS_UNDEFINED;
+ if (unlikely(prs->flags & JS_PROP_TMASK)) {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ desc->flags |= JS_PROP_GETSET;
+ if (pr->u.getset.getter)
+ desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+ if (pr->u.getset.setter)
+ desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ JSValue val = *pr->u.var_ref->pvalue;
+ if (unlikely(JS_IsUninitialized(val))) {
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ desc->value = JS_DupValue(ctx, val);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry */
+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
+ return -1;
+ goto retry;
+ }
+ } else {
+ desc->value = JS_DupValue(ctx, pr->u.value);
+ }
+ } else {
+ /* for consistency, send the exception even if desc is NULL */
+ if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {
+ if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* nothing to do: delay instantiation until actual value and/or attributes are read */
+ }
+ }
+ return TRUE;
+ }
+ if (p->is_exotic) {
+ if (p->fast_array) {
+ /* specific case for fast arrays */
+ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx;
+ idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ if (desc) {
+ desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
+ JS_PROP_CONFIGURABLE;
+ desc->getter = JS_UNDEFINED;
+ desc->setter = JS_UNDEFINED;
+ desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
+ }
+ return TRUE;
+ }
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->get_own_property) {
+ return em->get_own_property(ctx, desc,
+ JS_MKPTR(JS_TAG_OBJECT, p), prop);
+ }
+ }
+ }
+ return FALSE;
+}
+
+int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop)
+{
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);
+}
+
+/* return -1 if exception (Proxy object only) or TRUE/FALSE */
+int JS_IsExtensible(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (unlikely(p->class_id == JS_CLASS_PROXY))
+ return js_proxy_isExtensible(ctx, obj);
+ else
+ return p->extensible;
+}
+
+/* return -1 if exception (Proxy object only) or TRUE/FALSE */
+int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (unlikely(p->class_id == JS_CLASS_PROXY))
+ return js_proxy_preventExtensions(ctx, obj);
+ p->extensible = FALSE;
+ return TRUE;
+}
+
+/* return -1 if exception otherwise TRUE or FALSE */
+int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
+{
+ JSObject *p;
+ int ret;
+ JSValue obj1;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ for(;;) {
+ if (p->is_exotic) {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->has_property) {
+ /* has_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = em->has_property(ctx, obj1, prop);
+ JS_FreeValue(ctx, obj1);
+ return ret;
+ }
+ }
+ /* JS_GetOwnPropertyInternal can free the prototype */
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ if (ret != 0)
+ return ret;
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_AtomIsNumericIndex(ctx, prop);
+ if (ret != 0) {
+ if (ret < 0)
+ return -1;
+ return FALSE;
+ }
+ }
+ p = p->shape->proto;
+ if (!p)
+ break;
+ }
+ return FALSE;
+}
+
+/* val must be a symbol */
+static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)
+{
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ return js_get_atom_index(ctx->rt, p);
+}
+
+/* return JS_ATOM_NULL in case of exception */
+JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
+{
+ JSAtom atom;
+ uint32_t tag;
+ tag = JS_VALUE_GET_TAG(val);
+ if (tag == JS_TAG_INT &&
+ (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
+ /* fast path for integer values */
+ atom = JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
+ } else if (tag == JS_TAG_SYMBOL) {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
+ } else {
+ JSValue str;
+ str = JS_ToPropertyKey(ctx, val);
+ if (JS_IsException(str))
+ return JS_ATOM_NULL;
+ if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {
+ atom = js_symbol_to_atom(ctx, str);
+ } else {
+ atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));
+ }
+ }
+ return atom;
+}
+
+static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSValue prop)
+{
+ JSAtom atom;
+ JSValue ret;
+
+ if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
+ JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
+ JSObject *p;
+ uint32_t idx, len;
+ /* fast path for array access */
+ p = JS_VALUE_GET_OBJ(this_obj);
+ idx = JS_VALUE_GET_INT(prop);
+ len = p->u.array.count;
+ if (unlikely(idx >= len))
+ goto slow_path;
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY:
+ case JS_CLASS_ARGUMENTS:
+ return JS_DupValue(ctx, p->u.array.u.values[idx]);
+ case JS_CLASS_INT8_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);
+ case JS_CLASS_INT16_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);
+ case JS_CLASS_UINT16_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);
+ case JS_CLASS_INT32_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);
+ case JS_CLASS_UINT32_ARRAY:
+ return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ return JS_NewFloat64Impl(ctx, p->u.array.u.float_ptr[idx]);
+ case JS_CLASS_FLOAT64_ARRAY:
+ return JS_NewFloat64Impl(ctx, p->u.array.u.double_ptr[idx]);
+ default:
+ goto slow_path;
+ }
+ } else {
+ slow_path:
+ atom = JS_ValueToAtom(ctx, prop);
+ JS_FreeValue(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_GetProperty(ctx, this_obj, atom);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+ }
+}
+
+JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx)
+{
+ return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));
+}
+
+/* Check if an object has a generalized numeric property. Return value:
+ -1 for exception,
+ TRUE if property exists, stored into *pval,
+ FALSE if proprty does not exist.
+ */
+static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)
+{
+ JSValue val = JS_UNDEFINED;
+ JSAtom prop;
+ int present;
+
+ if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
+ /* fast path */
+ present = JS_HasProperty(ctx, obj, JS_AtomFromUInt32(idx));
+ if (present > 0) {
+ val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
+ if (unlikely(JS_IsException(val)))
+ present = -1;
+ }
+ } else {
+ prop = JS_NewAtomInt64(ctx, idx);
+ present = -1;
+ if (likely(prop != JS_ATOM_NULL)) {
+ present = JS_HasProperty(ctx, obj, prop);
+ if (present > 0) {
+ val = JS_GetProperty(ctx, obj, prop);
+ if (unlikely(JS_IsException(val)))
+ present = -1;
+ }
+ JS_FreeAtom(ctx, prop);
+ }
+ }
+ *pval = val;
+ return present;
+}
+
+static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)
+{
+ JSAtom prop;
+ JSValue val;
+
+ if ((uint64_t)idx <= INT32_MAX) {
+ /* fast path for fast arrays */
+ return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
+ }
+ prop = JS_NewAtomInt64(ctx, idx);
+ if (prop == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+
+ val = JS_GetProperty(ctx, obj, prop);
+ JS_FreeAtom(ctx, prop);
+ return val;
+}
+
+JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop)
+{
+ JSAtom atom;
+ JSValue ret;
+ atom = JS_NewAtom(ctx, prop);
+ ret = JS_GetProperty(ctx, this_obj, atom);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+/* Note: the property value is not initialized. Return NULL if memory
+ error. */
+static JSProperty *add_property(JSContext *ctx,
+ JSObject *p, JSAtom prop, int prop_flags)
+{
+ JSShape *sh, *new_sh;
+
+ sh = p->shape;
+ if (sh->is_hashed) {
+ /* try to find an existing shape */
+ new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);
+ if (new_sh) {
+ /* matching shape found: use it */
+ /* the property array may need to be resized */
+ if (new_sh->prop_size != sh->prop_size) {
+ JSProperty *new_prop;
+ new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *
+ new_sh->prop_size);
+ if (!new_prop)
+ return NULL;
+ p->prop = new_prop;
+ }
+ p->shape = js_dup_shape(new_sh);
+ js_free_shape(ctx->rt, sh);
+ return &p->prop[new_sh->prop_count - 1];
+ } else if (sh->header.ref_count != 1) {
+ /* if the shape is shared, clone it */
+ new_sh = js_clone_shape(ctx, sh);
+ if (!new_sh)
+ return NULL;
+ /* hash the cloned shape */
+ new_sh->is_hashed = TRUE;
+ js_shape_hash_link(ctx->rt, new_sh);
+ js_free_shape(ctx->rt, p->shape);
+ p->shape = new_sh;
+ }
+ }
+ assert(p->shape->header.ref_count == 1);
+ if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))
+ return NULL;
+ return &p->prop[p->shape->prop_count - 1];
+}
+
+/* can be called on Array or Arguments objects. return < 0 if
+ memory alloc error. */
+static no_inline warn_unused int convert_fast_array_to_array(JSContext *ctx,
+ JSObject *p)
+{
+ JSProperty *pr;
+ JSShape *sh;
+ JSValue *tab;
+ uint32_t i, len, new_count;
+
+ if (js_shape_prepare_update(ctx, p, NULL))
+ return -1;
+ len = p->u.array.count;
+ /* resize the properties once to simplify the error handling */
+ sh = p->shape;
+ new_count = sh->prop_count + len;
+ if (new_count > sh->prop_size) {
+ if (resize_properties(ctx, &p->shape, p, new_count))
+ return -1;
+ }
+
+ tab = p->u.array.u.values;
+ for(i = 0; i < len; i++) {
+ /* add_property cannot fail here but
+ JS_AtomFromUInt32(i) fails for i > INT32_MAX */
+ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E);
+ pr->u.value = *tab++;
+ }
+ js_free(ctx, p->u.array.u.values);
+ p->u.array.count = 0;
+ p->u.array.u.values = NULL; /* fail safe */
+ p->u.array.u1.size = 0;
+ p->fast_array = 0;
+ return 0;
+}
+
+static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
+{
+ JSShape *sh;
+ JSShapeProperty *pr, *lpr, *prop;
+ JSProperty *pr1;
+ uint32_t lpr_idx;
+ intptr_t h, h1;
+
+ redo:
+ sh = p->shape;
+ h1 = atom & sh->prop_hash_mask;
+ h = prop_hash_end(sh)[-h1 - 1];
+ prop = get_shape_prop(sh);
+ lpr = NULL;
+ lpr_idx = 0; /* prevent warning */
+ while (h != 0) {
+ pr = &prop[h - 1];
+ if (likely(pr->atom == atom)) {
+ /* found ! */
+ if (!(pr->flags & JS_PROP_CONFIGURABLE))
+ return FALSE;
+ /* realloc the shape if needed */
+ if (lpr)
+ lpr_idx = lpr - get_shape_prop(sh);
+ if (js_shape_prepare_update(ctx, p, &pr))
+ return -1;
+ sh = p->shape;
+ /* remove property */
+ if (lpr) {
+ lpr = get_shape_prop(sh) + lpr_idx;
+ lpr->hash_next = pr->hash_next;
+ } else {
+ prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
+ }
+ sh->deleted_prop_count++;
+ /* free the entry */
+ pr1 = &p->prop[h - 1];
+ free_property(ctx->rt, pr1, pr->flags);
+ JS_FreeAtom(ctx, pr->atom);
+ /* put default values */
+ pr->flags = 0;
+ pr->atom = JS_ATOM_NULL;
+ pr1->u.value = JS_UNDEFINED;
+
+ /* compact the properties if too many deleted properties */
+ if (sh->deleted_prop_count >= 8 &&
+ sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
+ compact_properties(ctx, p);
+ }
+ return TRUE;
+ }
+ lpr = pr;
+ h = pr->hash_next;
+ }
+
+ if (p->is_exotic) {
+ if (p->fast_array) {
+ uint32_t idx;
+ if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&
+ idx < p->u.array.count) {
+ if (p->class_id == JS_CLASS_ARRAY ||
+ p->class_id == JS_CLASS_ARGUMENTS) {
+ /* Special case deleting the last element of a fast Array */
+ if (idx == p->u.array.count - 1) {
+ JS_FreeValue(ctx, p->u.array.u.values[idx]);
+ p->u.array.count = idx;
+ return TRUE;
+ }
+ if (convert_fast_array_to_array(ctx, p))
+ return -1;
+ goto redo;
+ } else {
+ return FALSE;
+ }
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->delete_property) {
+ return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);
+ }
+ }
+ }
+ /* not found */
+ return TRUE;
+}
+
+static int call_setter(JSContext *ctx, JSObject *setter,
+ JSValueConst this_obj, JSValue val, int flags)
+{
+ JSValue ret, func;
+ if (likely(setter)) {
+ func = JS_MKPTR(JS_TAG_OBJECT, setter);
+ /* Note: the field could be removed in the setter */
+ func = JS_DupValue(ctx, func);
+ ret = JS_CallFree(ctx, func, this_obj, 1, &val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(ret))
+ return -1;
+ JS_FreeValue(ctx, ret);
+ return TRUE;
+ } else {
+ JS_FreeValue(ctx, val);
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ JS_ThrowTypeError(ctx, "no setter for property");
+ return -1;
+ }
+ return FALSE;
+ }
+}
+
+/* set the array length and remove the array elements if necessary. */
+static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
+ int flags)
+{
+ uint32_t len, idx, cur_len;
+ int i, ret;
+
+ /* Note: this call can reallocate the properties of 'p' */
+ ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
+ if (ret)
+ return -1;
+ /* JS_ToArrayLengthFree() must be done before the read-only test */
+ if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE)))
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
+
+ if (likely(p->fast_array)) {
+ uint32_t old_len = p->u.array.count;
+ if (len < old_len) {
+ for(i = len; i < old_len; i++) {
+ JS_FreeValue(ctx, p->u.array.u.values[i]);
+ }
+ p->u.array.count = len;
+ }
+ p->prop[0].u.value = JS_NewUint32(ctx, len);
+ } else {
+ /* Note: length is always a uint32 because the object is an
+ array */
+ JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
+ if (len < cur_len) {
+ uint32_t d;
+ JSShape *sh;
+ JSShapeProperty *pr;
+
+ d = cur_len - len;
+ sh = p->shape;
+ if (d <= sh->prop_count) {
+ JSAtom atom;
+
+ /* faster to iterate */
+ while (cur_len > len) {
+ atom = JS_NewAtomUInt32(ctx, cur_len - 1);
+ ret = delete_property(ctx, p, atom);
+ JS_FreeAtom(ctx, atom);
+ if (unlikely(!ret)) {
+ /* unlikely case: property is not
+ configurable */
+ break;
+ }
+ cur_len--;
+ }
+ } else {
+ /* faster to iterate thru all the properties. Need two
+ passes in case one of the property is not
+ configurable */
+ cur_len = len;
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
+ i++, pr++) {
+ if (pr->atom != JS_ATOM_NULL &&
+ JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
+ if (idx >= cur_len &&
+ !(pr->flags & JS_PROP_CONFIGURABLE)) {
+ cur_len = idx + 1;
+ }
+ }
+ }
+
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
+ i++, pr++) {
+ if (pr->atom != JS_ATOM_NULL &&
+ JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
+ if (idx >= cur_len) {
+ /* remove the property */
+ delete_property(ctx, p, pr->atom);
+ /* WARNING: the shape may have been modified */
+ sh = p->shape;
+ pr = get_shape_prop(sh) + i;
+ }
+ }
+ }
+ }
+ } else {
+ cur_len = len;
+ }
+ set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));
+ if (unlikely(cur_len > len)) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");
+ }
+ }
+ return TRUE;
+}
+
+/* return -1 if exception */
+static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len)
+{
+ uint32_t new_size;
+ size_t slack;
+ JSValue *new_array_prop;
+ /* XXX: potential arithmetic overflow */
+ new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);
+ new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);
+ if (!new_array_prop)
+ return -1;
+ new_size += slack / sizeof(*new_array_prop);
+ p->u.array.u.values = new_array_prop;
+ p->u.array.u1.size = new_size;
+ return 0;
+}
+
+/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =
+ TRUE and p->extensible = TRUE */
+static int add_fast_array_element(JSContext *ctx, JSObject *p,
+ JSValue val, int flags)
+{
+ uint32_t new_len, array_len;
+ /* extend the array by one */
+ /* XXX: convert to slow array if new_len > 2^31-1 elements */
+ new_len = p->u.array.count + 1;
+ /* update the length if necessary. We assume that if the length is
+ not an integer, then if it >= 2^31. */
+ if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
+ array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
+ if (new_len > array_len) {
+ if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
+ }
+ p->prop[0].u.value = JS_NewInt32(ctx, new_len);
+ }
+ }
+ if (unlikely(new_len > p->u.array.u1.size)) {
+ if (expand_fast_array(ctx, p, new_len)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ }
+ p->u.array.u.values[new_len - 1] = val;
+ p->u.array.count = new_len;
+ return TRUE;
+}
+
+static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
+{
+ JS_FreeValue(ctx, desc->getter);
+ JS_FreeValue(ctx, desc->setter);
+ JS_FreeValue(ctx, desc->value);
+}
+
+/* generic (and slower) version of JS_SetProperty() for
+ * Reflect.set(). 'obj' must be an object. */
+static int JS_SetPropertyGeneric(JSContext *ctx,
+ JSValueConst obj, JSAtom prop,
+ JSValue val, JSValueConst this_obj,
+ int flags)
+{
+ int ret;
+ JSPropertyDescriptor desc;
+ JSValue obj1;
+ JSObject *p;
+
+ obj1 = JS_DupValue(ctx, obj);
+ for(;;) {
+ p = JS_VALUE_GET_OBJ(obj1);
+ if (p->is_exotic) {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->set_property) {
+ ret = em->set_property(ctx, obj1, prop,
+ val, this_obj, flags);
+ JS_FreeValue(ctx, obj1);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ }
+
+ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (ret < 0) {
+ JS_FreeValue(ctx, obj1);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JSObject *setter;
+ if (JS_IsUndefined(desc.setter))
+ setter = NULL;
+ else
+ setter = JS_VALUE_GET_OBJ(desc.setter);
+ ret = call_setter(ctx, setter, this_obj, val, flags);
+ JS_FreeValue(ctx, desc.getter);
+ JS_FreeValue(ctx, desc.setter);
+ JS_FreeValue(ctx, obj1);
+ return ret;
+ } else {
+ JS_FreeValue(ctx, desc.value);
+ if (!(desc.flags & JS_PROP_WRITABLE)) {
+ JS_FreeValue(ctx, obj1);
+ goto read_only_error;
+ }
+ }
+ break;
+ }
+ /* Note: at this point 'obj1' cannot be a proxy. XXX: may have
+ to check recursion */
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsNull(obj1))
+ break;
+ }
+ JS_FreeValue(ctx, obj1);
+
+ if (!JS_IsObject(this_obj)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object");
+ }
+
+ p = JS_VALUE_GET_OBJ(this_obj);
+
+ /* modify the property in this_obj if it already exists */
+ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JS_FreeValue(ctx, desc.getter);
+ JS_FreeValue(ctx, desc.setter);
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
+ } else {
+ JS_FreeValue(ctx, desc.value);
+ if (!(desc.flags & JS_PROP_WRITABLE) ||
+ p->class_id == JS_CLASS_MODULE_NS) {
+ read_only_error:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
+ }
+ }
+ ret = JS_DefineProperty(ctx, this_obj, prop, val,
+ JS_UNDEFINED, JS_UNDEFINED,
+ JS_PROP_HAS_VALUE);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+
+ ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
+ flags |
+ JS_PROP_HAS_VALUE |
+ JS_PROP_HAS_ENUMERABLE |
+ JS_PROP_HAS_WRITABLE |
+ JS_PROP_HAS_CONFIGURABLE |
+ JS_PROP_C_W_E);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is
+ freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,
+ JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,
+ the new property is not added and an error is raised. */
+int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue val, int flags)
+{
+ JSObject *p, *p1;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ uint32_t tag;
+ JSPropertyDescriptor desc;
+ int ret;
+#if 0
+ printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");
+#endif
+ tag = JS_VALUE_GET_TAG(this_obj);
+ if (unlikely(tag != JS_TAG_OBJECT)) {
+ switch(tag) {
+ case JS_TAG_NULL:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
+ return -1;
+ case JS_TAG_UNDEFINED:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
+ return -1;
+ default:
+ /* even on a primitive type we can have setters on the prototype */
+ p = NULL;
+ p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));
+ goto prototype_lookup;
+ }
+ }
+ p = JS_VALUE_GET_OBJ(this_obj);
+retry:
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
+ JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
+ /* fast case */
+ set_value(ctx, &pr->u.value, val);
+ return TRUE;
+ } else if (prs->flags & JS_PROP_LENGTH) {
+ assert(p->class_id == JS_CLASS_ARRAY);
+ assert(prop == JS_ATOM_length);
+ return set_array_length(ctx, p, val, flags);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ /* JS_PROP_WRITABLE is always true for variable
+ references, but they are write protected in module name
+ spaces. */
+ if (p->class_id == JS_CLASS_MODULE_NS)
+ goto read_only_prop;
+ set_value(ctx, pr->u.var_ref->pvalue, val);
+ return TRUE;
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry (potentially useless) */
+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ goto retry;
+ } else {
+ goto read_only_prop;
+ }
+ }
+
+ p1 = p;
+ for(;;) {
+ if (p1->is_exotic) {
+ if (p1->fast_array) {
+ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx < p1->u.array.count) {
+ if (unlikely(p == p1))
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
+ else
+ break;
+ } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ goto typed_array_oob;
+ }
+ } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_AtomIsNumericIndex(ctx, prop);
+ if (ret != 0) {
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ typed_array_oob:
+ val = JS_ToNumberFree(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
+ }
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;
+ if (em) {
+ JSValue obj1;
+ if (em->set_property) {
+ /* set_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ ret = em->set_property(ctx, obj1, prop,
+ val, this_obj, flags);
+ JS_FreeValue(ctx, obj1);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (em->get_own_property) {
+ /* get_own_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ ret = em->get_own_property(ctx, &desc,
+ obj1, prop);
+ JS_FreeValue(ctx, obj1);
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JSObject *setter;
+ if (JS_IsUndefined(desc.setter))
+ setter = NULL;
+ else
+ setter = JS_VALUE_GET_OBJ(desc.setter);
+ ret = call_setter(ctx, setter, this_obj, val, flags);
+ JS_FreeValue(ctx, desc.getter);
+ JS_FreeValue(ctx, desc.setter);
+ return ret;
+ } else {
+ JS_FreeValue(ctx, desc.value);
+ if (!(desc.flags & JS_PROP_WRITABLE))
+ goto read_only_prop;
+ if (likely(p == p1)) {
+ ret = JS_DefineProperty(ctx, this_obj, prop, val,
+ JS_UNDEFINED, JS_UNDEFINED,
+ JS_PROP_HAS_VALUE);
+ JS_FreeValue(ctx, val);
+ return ret;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ p1 = p1->shape->proto;
+ prototype_lookup:
+ if (!p1)
+ break;
+
+ retry2:
+ prs = find_own_property(&pr, p1, prop);
+ if (prs) {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry (potentially useless) */
+ if (JS_AutoInitProperty(ctx, p1, prop, pr, prs))
+ return -1;
+ goto retry2;
+ } else if (!(prs->flags & JS_PROP_WRITABLE)) {
+ read_only_prop:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
+ }
+ }
+ }
+
+ if (unlikely(flags & JS_PROP_NO_ADD)) {
+ JS_FreeValue(ctx, val);
+ JS_ThrowReferenceErrorNotDefined(ctx, prop);
+ return -1;
+ }
+
+ if (unlikely(!p)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");
+ }
+
+ if (unlikely(!p->extensible)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
+ }
+
+ if (p->is_exotic) {
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
+ JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ /* fast case */
+ return add_fast_array_element(ctx, p, val, flags);
+ } else {
+ goto generic_create_prop;
+ }
+ } else {
+ generic_create_prop:
+ ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
+ flags |
+ JS_PROP_HAS_VALUE |
+ JS_PROP_HAS_ENUMERABLE |
+ JS_PROP_HAS_WRITABLE |
+ JS_PROP_HAS_CONFIGURABLE |
+ JS_PROP_C_W_E);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ }
+
+ pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
+ if (unlikely(!pr)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ pr->u.value = val;
+ return TRUE;
+}
+
+/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
+static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSValue prop, JSValue val, int flags)
+{
+ if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
+ JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
+ JSObject *p;
+ uint32_t idx;
+ double d;
+ int32_t v;
+
+ /* fast path for array access */
+ p = JS_VALUE_GET_OBJ(this_obj);
+ idx = JS_VALUE_GET_INT(prop);
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY:
+ if (unlikely(idx >= (uint32_t)p->u.array.count)) {
+ JSObject *p1;
+ JSShape *sh1;
+
+ /* fast path to add an element to the array */
+ if (idx != p->u.array.count ||
+ !p->fast_array || !p->extensible)
+ goto slow_path;
+ /* check if prototype chain has a numeric property */
+ p1 = p->shape->proto;
+ while (p1 != NULL) {
+ sh1 = p1->shape;
+ if (p1->class_id == JS_CLASS_ARRAY) {
+ if (unlikely(!p1->fast_array))
+ goto slow_path;
+ } else if (p1->class_id == JS_CLASS_OBJECT) {
+ if (unlikely(sh1->has_small_array_index))
+ goto slow_path;
+ } else {
+ goto slow_path;
+ }
+ p1 = sh1->proto;
+ }
+ /* add element */
+ return add_fast_array_element(ctx, p, val, flags);
+ }
+ set_value(ctx, &p->u.array.u.values[idx], val);
+ break;
+ case JS_CLASS_ARGUMENTS:
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto slow_path;
+ set_value(ctx, &p->u.array.u.values[idx], val);
+ break;
+ case JS_CLASS_UINT8C_ARRAY:
+ if (JS_ToUint8ClampFree(ctx, &v, val))
+ return -1;
+ /* Note: the conversion can detach the typed array, so the
+ array bound check must be done after */
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint8_ptr[idx] = v;
+ break;
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ if (JS_ToInt32Free(ctx, &v, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint8_ptr[idx] = v;
+ break;
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ if (JS_ToInt32Free(ctx, &v, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint16_ptr[idx] = v;
+ break;
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+ if (JS_ToInt32Free(ctx, &v, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint32_ptr[idx] = v;
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ /* XXX: need specific conversion function */
+ {
+ int64_t v;
+ if (JS_ToBigInt64Free(ctx, &v, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint64_ptr[idx] = v;
+ }
+ break;
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.float_ptr[idx] = d;
+ break;
+ case JS_CLASS_FLOAT64_ARRAY:
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count)) {
+ ta_out_of_bound:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
+ }
+ p->u.array.u.double_ptr[idx] = d;
+ break;
+ default:
+ goto slow_path;
+ }
+ return TRUE;
+ } else {
+ JSAtom atom;
+ int ret;
+ slow_path:
+ atom = JS_ValueToAtom(ctx, prop);
+ JS_FreeValue(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+ }
+}
+
+int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx, JSValue val)
+{
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,
+ JS_PROP_THROW);
+}
+
+int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
+ int64_t idx, JSValue val)
+{
+ JSAtom prop;
+ int res;
+
+ if ((uint64_t)idx <= INT32_MAX) {
+ /* fast path for fast arrays */
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,
+ JS_PROP_THROW);
+ }
+ prop = JS_NewAtomInt64(ctx, idx);
+ if (prop == JS_ATOM_NULL) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ res = JS_SetProperty(ctx, this_obj, prop, val);
+ JS_FreeAtom(ctx, prop);
+ return res;
+}
+
+int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop, JSValue val)
+{
+ JSAtom atom;
+ int ret;
+ atom = JS_NewAtom(ctx, prop);
+ ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+/* compute the property flags. For each flag: (JS_PROP_HAS_x forces
+ it, otherwise def_flags is used)
+ Note: makes assumption about the bit pattern of the flags
+*/
+static int get_prop_flags(int flags, int def_flags)
+{
+ int mask;
+ mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;
+ return (flags & mask) | (def_flags & ~mask);
+}
+
+static int JS_CreateProperty(JSContext *ctx, JSObject *p,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags)
+{
+ JSProperty *pr;
+ int ret, prop_flags;
+
+ /* add a new property or modify an existing exotic one */
+ if (p->is_exotic) {
+ if (p->class_id == JS_CLASS_ARRAY) {
+ uint32_t idx, len;
+
+ if (p->fast_array) {
+ if (JS_AtomIsTaggedInt(prop)) {
+ idx = JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ if (!p->extensible)
+ goto not_extensible;
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))
+ goto convert_to_array;
+ prop_flags = get_prop_flags(flags, 0);
+ if (prop_flags != JS_PROP_C_W_E)
+ goto convert_to_array;
+ return add_fast_array_element(ctx, p,
+ JS_DupValue(ctx, val), flags);
+ } else {
+ goto convert_to_array;
+ }
+ } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
+ /* convert the fast array to normal array */
+ convert_to_array:
+ if (convert_fast_array_to_array(ctx, p))
+ return -1;
+ goto generic_array;
+ }
+ } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
+ JSProperty *plen;
+ JSShapeProperty *pslen;
+ generic_array:
+ /* update the length field */
+ plen = &p->prop[0];
+ JS_ToUint32(ctx, &len, plen->u.value);
+ if ((idx + 1) > len) {
+ pslen = get_shape_prop(p->shape);
+ if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
+ /* XXX: should update the length after defining
+ the property */
+ len = idx + 1;
+ set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));
+ }
+ }
+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_AtomIsNumericIndex(ctx, prop);
+ if (ret != 0) {
+ if (ret < 0)
+ return -1;
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");
+ }
+ } else if (!(flags & JS_PROP_NO_EXOTIC)) {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em) {
+ if (em->define_own_property) {
+ return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),
+ prop, val, getter, setter, flags);
+ }
+ ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ if (ret < 0)
+ return -1;
+ if (!ret)
+ goto not_extensible;
+ }
+ }
+ }
+
+ if (!p->extensible) {
+ not_extensible:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
+ }
+
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
+ JS_PROP_GETSET;
+ } else {
+ prop_flags = flags & JS_PROP_C_W_E;
+ }
+ pr = add_property(ctx, p, prop, prop_flags);
+ if (unlikely(!pr))
+ return -1;
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ pr->u.getset.getter = NULL;
+ if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
+ pr->u.getset.getter =
+ JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));
+ }
+ pr->u.getset.setter = NULL;
+ if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {
+ pr->u.getset.setter =
+ JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
+ }
+ } else {
+ if (flags & JS_PROP_HAS_VALUE) {
+ pr->u.value = JS_DupValue(ctx, val);
+ } else {
+ pr->u.value = JS_UNDEFINED;
+ }
+ }
+ return TRUE;
+}
+
+/* return FALSE if not OK */
+static BOOL check_define_prop_flags(int prop_flags, int flags)
+{
+ BOOL has_accessor, is_getset;
+
+ if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
+ if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==
+ (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {
+ return FALSE;
+ }
+ if ((flags & JS_PROP_HAS_ENUMERABLE) &&
+ (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))
+ return FALSE;
+ }
+ if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
+ has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);
+ is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);
+ if (has_accessor != is_getset)
+ return FALSE;
+ if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) {
+ /* not writable: cannot set the writable bit */
+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
+ (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/* ensure that the shape can be safely modified */
+static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
+ JSShapeProperty **pprs)
+{
+ JSShape *sh;
+ uint32_t idx = 0; /* prevent warning */
+
+ sh = p->shape;
+ if (sh->is_hashed) {
+ if (sh->header.ref_count != 1) {
+ if (pprs)
+ idx = *pprs - get_shape_prop(sh);
+ /* clone the shape (the resulting one is no longer hashed) */
+ sh = js_clone_shape(ctx, sh);
+ if (!sh)
+ return -1;
+ js_free_shape(ctx->rt, p->shape);
+ p->shape = sh;
+ if (pprs)
+ *pprs = get_shape_prop(sh) + idx;
+ } else {
+ js_shape_hash_unlink(ctx->rt, sh);
+ sh->is_hashed = FALSE;
+ }
+ }
+ return 0;
+}
+
+static int js_update_property_flags(JSContext *ctx, JSObject *p,
+ JSShapeProperty **pprs, int flags)
+{
+ if (flags != (*pprs)->flags) {
+ if (js_shape_prepare_update(ctx, p, pprs))
+ return -1;
+ (*pprs)->flags = flags;
+ }
+ return 0;
+}
+
+/* allowed flags:
+ JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE
+ JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,
+ JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,
+ JS_PROP_THROW, JS_PROP_NO_EXOTIC.
+ If JS_PROP_THROW is set, return an exception instead of FALSE.
+ if JS_PROP_NO_EXOTIC is set, do not call the exotic
+ define_own_property callback.
+ return -1 (exception), FALSE or TRUE.
+*/
+int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter, int flags)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ int mask, res;
+
+ if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ p = JS_VALUE_GET_OBJ(this_obj);
+
+ redo_prop_update:
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* the range of the Array length property is always tested before */
+ if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {
+ uint32_t array_length;
+ if (JS_ToArrayLengthFree(ctx, &array_length,
+ JS_DupValue(ctx, val), FALSE)) {
+ return -1;
+ }
+ /* this code relies on the fact that Uint32 are never allocated */
+ val = JS_NewUint32(ctx, array_length);
+ /* prs may have been modified */
+ prs = find_own_property(&pr, p, prop);
+ assert(prs != NULL);
+ }
+ /* property already exists */
+ if (!check_define_prop_flags(prs->flags, flags)) {
+ not_configurable:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
+ }
+
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry */
+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
+ return -1;
+ goto redo_prop_update;
+ }
+
+ if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ JSObject *new_getter, *new_setter;
+
+ if (JS_IsFunction(ctx, getter)) {
+ new_getter = JS_VALUE_GET_OBJ(getter);
+ } else {
+ new_getter = NULL;
+ }
+ if (JS_IsFunction(ctx, setter)) {
+ new_setter = JS_VALUE_GET_OBJ(setter);
+ } else {
+ new_setter = NULL;
+ }
+
+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {
+ if (js_shape_prepare_update(ctx, p, &prs))
+ return -1;
+ /* convert to getset */
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ free_var_ref(ctx->rt, pr->u.var_ref);
+ } else {
+ JS_FreeValue(ctx, pr->u.value);
+ }
+ prs->flags = (prs->flags &
+ (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
+ JS_PROP_GETSET;
+ pr->u.getset.getter = NULL;
+ pr->u.getset.setter = NULL;
+ } else {
+ if (!(prs->flags & JS_PROP_CONFIGURABLE)) {
+ if ((flags & JS_PROP_HAS_GET) &&
+ new_getter != pr->u.getset.getter) {
+ goto not_configurable;
+ }
+ if ((flags & JS_PROP_HAS_SET) &&
+ new_setter != pr->u.getset.setter) {
+ goto not_configurable;
+ }
+ }
+ }
+ if (flags & JS_PROP_HAS_GET) {
+ if (pr->u.getset.getter)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+ if (new_getter)
+ JS_DupValue(ctx, getter);
+ pr->u.getset.getter = new_getter;
+ }
+ if (flags & JS_PROP_HAS_SET) {
+ if (pr->u.getset.setter)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+ if (new_setter)
+ JS_DupValue(ctx, setter);
+ pr->u.getset.setter = new_setter;
+ }
+ } else {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ /* convert to data descriptor */
+ if (js_shape_prepare_update(ctx, p, &prs))
+ return -1;
+ if (pr->u.getset.getter)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+ if (pr->u.getset.setter)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+ prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
+ pr->u.value = JS_UNDEFINED;
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ /* Note: JS_PROP_VARREF is always writable */
+ } else {
+ if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
+ (flags & JS_PROP_HAS_VALUE)) {
+ if (!js_same_value(ctx, val, pr->u.value)) {
+ goto not_configurable;
+ } else {
+ return TRUE;
+ }
+ }
+ }
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ if (flags & JS_PROP_HAS_VALUE) {
+ if (p->class_id == JS_CLASS_MODULE_NS) {
+ /* JS_PROP_WRITABLE is always true for variable
+ references, but they are write protected in module name
+ spaces. */
+ if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))
+ goto not_configurable;
+ }
+ /* update the reference */
+ set_value(ctx, pr->u.var_ref->pvalue,
+ JS_DupValue(ctx, val));
+ }
+ /* if writable is set to false, no longer a
+ reference (for mapped arguments) */
+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
+ JSValue val1;
+ if (js_shape_prepare_update(ctx, p, &prs))
+ return -1;
+ val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
+ free_var_ref(ctx->rt, pr->u.var_ref);
+ pr->u.value = val1;
+ prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
+ }
+ } else if (prs->flags & JS_PROP_LENGTH) {
+ if (flags & JS_PROP_HAS_VALUE) {
+ /* Note: no JS code is executable because
+ 'val' is guaranted to be a Uint32 */
+ res = set_array_length(ctx, p, JS_DupValue(ctx, val),
+ flags);
+ } else {
+ res = TRUE;
+ }
+ /* still need to reset the writable flag if
+ needed. The JS_PROP_LENGTH is kept because the
+ Uint32 test is still done if the length
+ property is read-only. */
+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
+ JS_PROP_HAS_WRITABLE) {
+ prs = get_shape_prop(p->shape);
+ if (js_update_property_flags(ctx, p, &prs,
+ prs->flags & ~JS_PROP_WRITABLE))
+ return -1;
+ }
+ return res;
+ } else {
+ if (flags & JS_PROP_HAS_VALUE) {
+ JS_FreeValue(ctx, pr->u.value);
+ pr->u.value = JS_DupValue(ctx, val);
+ }
+ if (flags & JS_PROP_HAS_WRITABLE) {
+ if (js_update_property_flags(ctx, p, &prs,
+ (prs->flags & ~JS_PROP_WRITABLE) |
+ (flags & JS_PROP_WRITABLE)))
+ return -1;
+ }
+ }
+ }
+ }
+ mask = 0;
+ if (flags & JS_PROP_HAS_CONFIGURABLE)
+ mask |= JS_PROP_CONFIGURABLE;
+ if (flags & JS_PROP_HAS_ENUMERABLE)
+ mask |= JS_PROP_ENUMERABLE;
+ if (js_update_property_flags(ctx, p, &prs,
+ (prs->flags & ~mask) | (flags & mask)))
+ return -1;
+ return TRUE;
+ }
+
+ /* handle modification of fast array elements */
+ if (p->fast_array) {
+ uint32_t idx;
+ uint32_t prop_flags;
+ if (p->class_id == JS_CLASS_ARRAY) {
+ if (JS_AtomIsTaggedInt(prop)) {
+ idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
+ if (prop_flags != JS_PROP_C_W_E)
+ goto convert_to_slow_array;
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ convert_to_slow_array:
+ if (convert_fast_array_to_array(ctx, p))
+ return -1;
+ else
+ goto redo_prop_update;
+ }
+ if (flags & JS_PROP_HAS_VALUE) {
+ set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));
+ }
+ return TRUE;
+ }
+ }
+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ JSValue num;
+ int ret;
+
+ if (!JS_AtomIsTaggedInt(prop)) {
+ /* slow path with to handle all numeric indexes */
+ num = JS_AtomIsNumericIndex1(ctx, prop);
+ if (JS_IsUndefined(num))
+ goto typed_array_done;
+ if (JS_IsException(num))
+ return -1;
+ ret = JS_NumberIsInteger(ctx, num);
+ if (ret < 0) {
+ JS_FreeValue(ctx, num);
+ return -1;
+ }
+ if (!ret) {
+ JS_FreeValue(ctx, num);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");
+ }
+ ret = JS_NumberIsNegativeOrMinusZero(ctx, num);
+ JS_FreeValue(ctx, num);
+ if (ret) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
+ }
+ if (!JS_AtomIsTaggedInt(prop))
+ goto typed_array_oob;
+ }
+ idx = JS_AtomToUInt32(prop);
+ /* if the typed array is detached, p->u.array.count = 0 */
+ if (idx >= typed_array_get_length(ctx, p)) {
+ typed_array_oob:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");
+ }
+ prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||
+ prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");
+ }
+ if (flags & JS_PROP_HAS_VALUE) {
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);
+ }
+ return TRUE;
+ typed_array_done: ;
+ }
+ }
+
+ return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);
+}
+
+static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSAutoInitIDEnum id,
+ void *opaque, int flags)
+{
+ JSObject *p;
+ JSProperty *pr;
+
+ if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
+ return FALSE;
+
+ p = JS_VALUE_GET_OBJ(this_obj);
+
+ if (find_own_property(&pr, p, prop)) {
+ /* property already exists */
+ abort();
+ return FALSE;
+ }
+
+ /* Specialized CreateProperty */
+ pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
+ if (unlikely(!pr))
+ return -1;
+ pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
+ assert((pr->u.init.realm_and_id & 3) == 0);
+ assert(id <= 3);
+ pr->u.init.realm_and_id |= id;
+ pr->u.init.opaque = opaque;
+ return TRUE;
+}
+
+/* shortcut to add or redefine a new property value */
+int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue val, int flags)
+{
+ int ret;
+ ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,
+ flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,
+ JSValue prop, JSValue val, int flags)
+{
+ JSAtom atom;
+ int ret;
+ atom = JS_ValueToAtom(ctx, prop);
+ JS_FreeValue(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx, JSValue val, int flags)
+{
+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),
+ val, flags);
+}
+
+int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,
+ int64_t idx, JSValue val, int flags)
+{
+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
+ val, flags);
+}
+
+int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop, JSValue val, int flags)
+{
+ JSAtom atom;
+ int ret;
+ atom = JS_NewAtom(ctx, prop);
+ ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+/* shortcut to add getter & setter */
+int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue getter, JSValue setter,
+ int flags)
+{
+ int ret;
+ ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,
+ flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);
+ JS_FreeValue(ctx, getter);
+ JS_FreeValue(ctx, setter);
+ return ret;
+}
+
+static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,
+ int64_t idx, JSValue val, int flags)
+{
+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
+ val, flags | JS_PROP_CONFIGURABLE |
+ JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);
+}
+
+
+/* return TRUE if 'obj' has a non empty 'name' string */
+static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)
+{
+ JSProperty *pr;
+ JSShapeProperty *prs;
+ JSValueConst val;
+ JSString *p;
+
+ prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);
+ if (!prs)
+ return FALSE;
+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
+ return TRUE;
+ val = pr->u.value;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
+ return TRUE;
+ p = JS_VALUE_GET_STRING(val);
+ return (p->len != 0);
+}
+
+static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,
+ JSAtom name, int flags)
+{
+ if (name != JS_ATOM_NULL
+ && JS_IsObject(obj)
+ && !js_object_has_name(ctx, obj)
+ && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,
+ JSValueConst str, int flags)
+{
+ if (JS_IsObject(obj) &&
+ !js_object_has_name(ctx, obj)) {
+ JSAtom prop;
+ JSValue name_str;
+ prop = JS_ValueToAtom(ctx, str);
+ if (prop == JS_ATOM_NULL)
+ return -1;
+ name_str = js_get_function_name(ctx, prop);
+ JS_FreeAtom(ctx, prop);
+ if (JS_IsException(name_str))
+ return -1;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+#define DEFINE_GLOBAL_LEX_VAR (1 << 7)
+#define DEFINE_GLOBAL_FUNC_VAR (1 << 6)
+
+static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
+{
+ return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
+}
+
+/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
+/* XXX: could support exotic global object. */
+static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ prs = find_own_property1(p, prop);
+ /* XXX: should handle JS_PROP_AUTOINIT */
+ if (flags & DEFINE_GLOBAL_LEX_VAR) {
+ if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))
+ goto fail_redeclaration;
+ } else {
+ if (!prs && !p->extensible)
+ goto define_error;
+ if (flags & DEFINE_GLOBAL_FUNC_VAR) {
+ if (prs) {
+ if (!(prs->flags & JS_PROP_CONFIGURABLE) &&
+ ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||
+ ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
+ (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
+ define_error:
+ JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
+ prop);
+ return -1;
+ }
+ }
+ }
+ }
+ /* check if there already is a lexical declaration */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property1(p, prop);
+ if (prs) {
+ fail_redeclaration:
+ JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);
+ return -1;
+ }
+ return 0;
+}
+
+/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */
+/* XXX: could support exotic global object. */
+static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSValue val;
+ int flags;
+
+ if (def_flags & DEFINE_GLOBAL_LEX_VAR) {
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |
+ JS_PROP_CONFIGURABLE;
+ val = JS_UNINITIALIZED;
+ } else {
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
+ (def_flags & JS_PROP_CONFIGURABLE);
+ val = JS_UNDEFINED;
+ }
+ prs = find_own_property1(p, prop);
+ if (prs)
+ return 0;
+ if (!p->extensible)
+ return 0;
+ pr = add_property(ctx, p, prop, flags);
+ if (unlikely(!pr))
+ return -1;
+ pr->u.value = val;
+ return 0;
+}
+
+/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */
+/* XXX: could support exotic global object. */
+static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
+ JSValueConst func, int def_flags)
+{
+
+ JSObject *p;
+ JSShapeProperty *prs;
+ int flags;
+
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ prs = find_own_property1(p, prop);
+ flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;
+ if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {
+ flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;
+ }
+ if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,
+ JS_UNDEFINED, JS_UNDEFINED, flags) < 0)
+ return -1;
+ return 0;
+}
+
+static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
+ BOOL throw_ref_error)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+
+ /* no exotic behavior is possible in global_var_obj */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* XXX: should handle JS_PROP_TMASK properties */
+ if (unlikely(JS_IsUninitialized(pr->u.value)))
+ return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return JS_DupValue(ctx, pr->u.value);
+ }
+ if (ctx->scopeLookup) {
+ struct LookupResult result = ctx->scopeLookup(ctx, prop);
+ if (result.useResult)
+ return result.value;
+ }
+ return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
+ ctx->global_obj, throw_ref_error);
+}
+
+/* construct a reference to a global variable */
+static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+
+ /* no exotic behavior is possible in global_var_obj */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* XXX: should handle JS_PROP_AUTOINIT properties? */
+ /* XXX: conformance: do these tests in
+ OP_put_var_ref/OP_get_var_ref ? */
+ if (unlikely(JS_IsUninitialized(pr->u.value))) {
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
+ return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
+ }
+ sp[0] = JS_DupValue(ctx, ctx->global_var_obj);
+ } else {
+ int ret;
+ ret = JS_HasProperty(ctx, ctx->global_obj, prop);
+ if (ret < 0)
+ return -1;
+ if (ret) {
+ sp[0] = JS_DupValue(ctx, ctx->global_obj);
+ } else {
+ sp[0] = JS_UNDEFINED;
+ }
+ }
+ sp[1] = JS_AtomToValue(ctx, prop);
+ return 0;
+}
+
+/* use for strict variable access: test if the variable exists */
+static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ int ret;
+
+ /* no exotic behavior is possible in global_var_obj */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property1(p, prop);
+ if (prs) {
+ ret = TRUE;
+ } else {
+ ret = JS_HasProperty(ctx, ctx->global_obj, prop);
+ if (ret < 0)
+ return -1;
+ }
+ return ret;
+}
+
+/* flag = 0: normal variable write
+ flag = 1: initialize lexical variable
+ flag = 2: normal variable write, strict check was done before
+*/
+static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
+ int flag)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ int flags;
+
+ /* no exotic behavior is possible in global_var_obj */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* XXX: should handle JS_PROP_AUTOINIT properties? */
+ if (flag != 1) {
+ if (unlikely(JS_IsUninitialized(pr->u.value))) {
+ JS_FreeValue(ctx, val);
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
+ }
+ }
+ set_value(ctx, &pr->u.value, val);
+ return 0;
+ }
+ flags = JS_PROP_THROW_STRICT;
+ if (is_strict_mode(ctx))
+ flags |= JS_PROP_NO_ADD;
+
+ if (ctx->scopeLookup) {
+ struct LookupResult result = ctx->scopeLookup(ctx, prop);
+ if (result.useResult) {
+ JS_FreeValue(ctx, result.value);
+ return JS_SetPropertyInternal(ctx, result.scope, prop, val, flags);
+ }
+ }
+
+ return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);
+}
+
+/* return -1, FALSE or TRUE. return FALSE if not configurable or
+ invalid object. return -1 in case of exception.
+ flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */
+int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)
+{
+ JSValue obj1;
+ JSObject *p;
+ int res;
+
+ obj1 = JS_ToObject(ctx, obj);
+ if (JS_IsException(obj1))
+ return -1;
+ p = JS_VALUE_GET_OBJ(obj1);
+ res = delete_property(ctx, p, prop);
+ JS_FreeValue(ctx, obj1);
+ if (res != FALSE)
+ return res;
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ JS_ThrowTypeError(ctx, "could not delete property");
+ return -1;
+ }
+ return FALSE;
+}
+
+int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)
+{
+ JSAtom prop;
+ int res;
+
+ if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
+ /* fast path for fast arrays */
+ return JS_DeleteProperty(ctx, obj, JS_AtomFromUInt32(idx), flags);
+ }
+ prop = JS_NewAtomInt64(ctx, idx);
+ if (prop == JS_ATOM_NULL)
+ return -1;
+ res = JS_DeleteProperty(ctx, obj, prop, flags);
+ JS_FreeAtom(ctx, prop);
+ return res;
+}
+
+BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ switch(p->class_id) {
+ case JS_CLASS_BYTECODE_FUNCTION:
+ return TRUE;
+ case JS_CLASS_PROXY:
+ return p->u.proxy_data->is_func;
+ default:
+ return (ctx->rt->class_array[p->class_id].call != NULL);
+ }
+}
+
+BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ if (p->class_id == JS_CLASS_C_FUNCTION)
+ return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);
+ else
+ return FALSE;
+}
+
+BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ return p->is_constructor;
+}
+
+BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->is_constructor = val;
+ return TRUE;
+}
+
+BOOL JS_IsError(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ return (p->class_id == JS_CLASS_ERROR);
+}
+
+/* used to avoid catching interrupt exceptions */
+BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;
+}
+
+void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return;
+ p = JS_VALUE_GET_OBJ(val);
+ if (p->class_id == JS_CLASS_ERROR)
+ p->is_uncatchable_error = flag;
+}
+
+void JS_ResetUncatchableError(JSContext *ctx)
+{
+ JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);
+}
+
+void JS_SetOpaque(JSValue obj, void *opaque)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(obj);
+ p->u.opaque = opaque;
+ }
+}
+
+/* return NULL if not an object of class class_id */
+void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return NULL;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id != class_id)
+ return NULL;
+ return p->u.opaque;
+}
+
+void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
+{
+ void *p = JS_GetOpaque(obj, class_id);
+ if (unlikely(!p)) {
+ JS_ThrowTypeErrorInvalidClass(ctx, class_id);
+ }
+ return p;
+}
+
+#define HINT_STRING 0
+#define HINT_NUMBER 1
+#define HINT_NONE 2
+/* don't try Symbol.toPrimitive */
+#define HINT_FORCE_ORDINARY (1 << 4)
+
+static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
+{
+ int i;
+ BOOL force_ordinary;
+
+ JSAtom method_name;
+ JSValue method, ret;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return val;
+ force_ordinary = hint & HINT_FORCE_ORDINARY;
+ hint &= ~HINT_FORCE_ORDINARY;
+ if (!force_ordinary) {
+ method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);
+ if (JS_IsException(method))
+ goto exception;
+ /* ECMA says *If exoticToPrim is not undefined* but tests in
+ test262 use null as a non callable converter */
+ if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
+ JSAtom atom;
+ JSValue arg;
+ switch(hint) {
+ case HINT_STRING:
+ atom = JS_ATOM_string;
+ break;
+ case HINT_NUMBER:
+ atom = JS_ATOM_number;
+ break;
+ default:
+ case HINT_NONE:
+ atom = JS_ATOM_default;
+ break;
+ }
+ arg = JS_AtomToString(ctx, atom);
+ ret = JS_CallFree(ctx, method, val, 1, &arg);
+ JS_FreeValue(ctx, arg);
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, val);
+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)
+ return ret;
+ JS_FreeValue(ctx, ret);
+ return JS_ThrowTypeError(ctx, "toPrimitive");
+ }
+ }
+ if (hint != HINT_STRING)
+ hint = HINT_NUMBER;
+ for(i = 0; i < 2; i++) {
+ if ((i ^ hint) == 0) {
+ method_name = JS_ATOM_toString;
+ } else {
+ method_name = JS_ATOM_valueOf;
+ }
+ method = JS_GetProperty(ctx, val, method_name);
+ if (JS_IsException(method))
+ goto exception;
+ if (JS_IsFunction(ctx, method)) {
+ ret = JS_CallFree(ctx, method, val, 0, NULL);
+ if (JS_IsException(ret))
+ goto exception;
+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ JS_FreeValue(ctx, ret);
+ } else {
+ JS_FreeValue(ctx, method);
+ }
+ }
+ JS_ThrowTypeError(ctx, "toPrimitive");
+exception:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
+{
+ return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
+}
+
+void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return;
+ p = JS_VALUE_GET_OBJ(obj);
+ p->is_HTMLDDA = TRUE;
+}
+
+static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ return p->is_HTMLDDA;
+}
+
+static int JS_ToBoolFree(JSContext *ctx, JSValue val)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ return JS_VALUE_GET_INT(val) != 0;
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ return JS_VALUE_GET_INT(val);
+ case JS_TAG_EXCEPTION:
+ return -1;
+ case JS_TAG_STRING:
+ {
+ BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ BOOL ret;
+ ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ BOOL ret;
+ ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+#endif
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ BOOL ret;
+ ret = !p->is_HTMLDDA;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ break;
+ default:
+ if (JS_TAG_IS_FLOAT64(tag)) {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ return !isnan(d) && d != 0;
+ } else {
+ JS_FreeValue(ctx, val);
+ return TRUE;
+ }
+ }
+}
+
+int JS_ToBool(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));
+}
+
+static int skip_spaces(const char *pc)
+{
+ const uint8_t *p, *p_next, *p_start;
+ uint32_t c;
+
+ p = p_start = (const uint8_t *)pc;
+ for (;;) {
+ c = *p;
+ if (c < 128) {
+ if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))
+ break;
+ p++;
+ } else {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
+ if (!lre_is_space(c))
+ break;
+ p = p_next;
+ }
+ }
+ return p - p_start;
+}
+
+static inline int to_digit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ else if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ else
+ return 36;
+}
+
+/* XXX: remove */
+static double js_strtod(const char *p, int radix, BOOL is_float)
+{
+ double d;
+ int c;
+
+ if (!is_float || radix != 10) {
+ uint64_t n_max, n;
+ int int_exp, is_neg;
+
+ is_neg = 0;
+ if (*p == '-') {
+ is_neg = 1;
+ p++;
+ }
+
+ /* skip leading zeros */
+ while (*p == '0')
+ p++;
+ n = 0;
+ if (radix == 10)
+ n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
+ else
+ n_max = ((uint64_t)-1 - (radix - 1)) / radix;
+ /* XXX: could be more precise */
+ int_exp = 0;
+ while (*p != '\0') {
+ c = to_digit((uint8_t)*p);
+ if (c >= radix)
+ break;
+ if (n <= n_max) {
+ n = n * radix + c;
+ } else {
+ int_exp++;
+ }
+ p++;
+ }
+ d = n;
+ if (int_exp != 0) {
+ d *= pow(radix, int_exp);
+ }
+ if (is_neg)
+ d = -d;
+ } else {
+ d = safe_strtod(p, NULL);
+ }
+ return d;
+}
+
+#define ATOD_INT_ONLY (1 << 0)
+/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
+#define ATOD_ACCEPT_BIN_OCT (1 << 2)
+/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
+#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4)
+/* accept _ between digits as a digit separator */
+#define ATOD_ACCEPT_UNDERSCORES (1 << 5)
+/* allow a suffix to override the type */
+#define ATOD_ACCEPT_SUFFIX (1 << 6)
+/* default type */
+#define ATOD_TYPE_MASK (3 << 7)
+#define ATOD_TYPE_FLOAT64 (0 << 7)
+#define ATOD_TYPE_BIG_INT (1 << 7)
+#define ATOD_TYPE_BIG_FLOAT (2 << 7)
+#define ATOD_TYPE_BIG_DECIMAL (3 << 7)
+/* assume bigint mode: floats are parsed as integers if no decimal
+ point nor exponent */
+#define ATOD_MODE_BIGINT (1 << 9)
+/* accept -0x1 */
+#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
+
+#ifdef CONFIG_BIGNUM
+static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent)
+{
+ bf_t a_s, *a = &a_s;
+ int ret;
+ JSValue val;
+ val = JS_NewBigInt(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigInt(val);
+ ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);
+ if (ret & BF_ST_MEM_ERROR) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
+ return val;
+}
+
+static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent)
+{
+ bf_t *a;
+ int ret;
+ JSValue val;
+
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigFloat(val);
+ if (flags & ATOD_ACCEPT_SUFFIX) {
+ /* return the exponent to get infinite precision */
+ ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,
+ BF_RNDZ | BF_ATOF_EXPONENT);
+ } else {
+ ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,
+ ctx->fp_env.flags);
+ }
+ if (ret & BF_ST_MEM_ERROR) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ return val;
+}
+
+static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent)
+{
+ bfdec_t *a;
+ int ret;
+ JSValue val;
+
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigDecimal(val);
+ ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
+ BF_RNDZ | BF_ATOF_NO_NAN_INF);
+ if (ret & BF_ST_MEM_ERROR) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ return val;
+}
+
+#endif
+
+/* return an exception in case of memory error. Return JS_NAN if
+ invalid syntax */
+#ifdef CONFIG_BIGNUM
+static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,
+ int radix, int flags, slimb_t *pexponent)
+#else
+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ int radix, int flags)
+#endif
+{
+ const char *p, *p_start;
+ int sep, is_neg;
+ BOOL is_float, has_legacy_octal;
+ int atod_type = flags & ATOD_TYPE_MASK;
+ char buf1[64], *buf;
+ int i, j, len;
+ BOOL buf_allocated = FALSE;
+ JSValue val;
+
+ /* optional separator between digits */
+ sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
+ has_legacy_octal = FALSE;
+
+ p = str;
+ p_start = p;
+ is_neg = 0;
+ if (p[0] == '+') {
+ p++;
+ p_start++;
+ if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+ goto no_radix_prefix;
+ } else if (p[0] == '-') {
+ p++;
+ p_start++;
+ is_neg = 1;
+ if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+ goto no_radix_prefix;
+ }
+ if (p[0] == '0') {
+ if ((p[1] == 'x' || p[1] == 'X') &&
+ (radix == 0 || radix == 16)) {
+ p += 2;
+ radix = 16;
+ } else if ((p[1] == 'o' || p[1] == 'O') &&
+ radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 8;
+ } else if ((p[1] == 'b' || p[1] == 'B') &&
+ radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 2;
+ } else if ((p[1] >= '0' && p[1] <= '9') &&
+ radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
+ int i;
+ has_legacy_octal = TRUE;
+ sep = 256;
+ for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
+ continue;
+ if (p[i] == '8' || p[i] == '9')
+ goto no_prefix;
+ p += 1;
+ radix = 8;
+ } else {
+ goto no_prefix;
+ }
+ /* there must be a digit after the prefix */
+ if (to_digit((uint8_t)*p) >= radix)
+ goto fail;
+ no_prefix: ;
+ } else {
+ no_radix_prefix:
+ if (!(flags & ATOD_INT_ONLY) &&
+ (atod_type == ATOD_TYPE_FLOAT64 ||
+ atod_type == ATOD_TYPE_BIG_FLOAT) &&
+ strstart(p, "Infinity", &p)) {
+#ifdef CONFIG_BIGNUM
+ if (atod_type == ATOD_TYPE_BIG_FLOAT) {
+ bf_t *a;
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ goto done;
+ a = JS_GetBigFloat(val);
+ bf_set_inf(a, is_neg);
+ } else
+#endif
+ {
+ double d = INFINITY;
+ if (is_neg)
+ d = -d;
+ val = JS_NewFloat64(ctx, d);
+ }
+ goto done;
+ }
+ }
+ if (radix == 0)
+ radix = 10;
+ is_float = FALSE;
+ p_start = p;
+ while (to_digit((uint8_t)*p) < radix
+ || (*p == sep && (radix != 10 ||
+ p != p_start + 1 || p[-1] != '0') &&
+ to_digit((uint8_t)p[1]) < radix)) {
+ p++;
+ }
+ if (!(flags & ATOD_INT_ONLY)) {
+ if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
+ is_float = TRUE;
+ p++;
+ if (*p == sep)
+ goto fail;
+ while (to_digit((uint8_t)*p) < radix ||
+ (*p == sep && to_digit((uint8_t)p[1]) < radix))
+ p++;
+ }
+ if (p > p_start &&
+ (((*p == 'e' || *p == 'E') && radix == 10) ||
+ ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
+ const char *p1 = p + 1;
+ is_float = TRUE;
+ if (*p1 == '+' || *p1 == '-')
+ p1++;
+ if (is_digit((uint8_t)*p1)) {
+ p = p1 + 1;
+ while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
+ p++;
+ }
+ }
+ }
+ if (p == p_start)
+ goto fail;
+
+ buf = buf1;
+ buf_allocated = FALSE;
+ len = p - p_start;
+ if (unlikely((len + 2) > sizeof(buf1))) {
+ buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
+ if (!buf)
+ goto mem_error;
+ buf_allocated = TRUE;
+ }
+ /* remove the separators and the radix prefixes */
+ j = 0;
+ if (is_neg)
+ buf[j++] = '-';
+ for (i = 0; i < len; i++) {
+ if (p_start[i] != '_')
+ buf[j++] = p_start[i];
+ }
+ buf[j] = '\0';
+
+#ifdef CONFIG_BIGNUM
+ if (flags & ATOD_ACCEPT_SUFFIX) {
+ if (*p == 'n') {
+ p++;
+ atod_type = ATOD_TYPE_BIG_INT;
+ } else if (*p == 'l') {
+ p++;
+ atod_type = ATOD_TYPE_BIG_FLOAT;
+ } else if (*p == 'm') {
+ p++;
+ atod_type = ATOD_TYPE_BIG_DECIMAL;
+ } else {
+ if (flags & ATOD_MODE_BIGINT) {
+ if (!is_float)
+ atod_type = ATOD_TYPE_BIG_INT;
+ if (has_legacy_octal)
+ goto fail;
+ } else {
+ if (is_float && radix != 10)
+ goto fail;
+ }
+ }
+ } else {
+ if (atod_type == ATOD_TYPE_FLOAT64) {
+ if (flags & ATOD_MODE_BIGINT) {
+ if (!is_float)
+ atod_type = ATOD_TYPE_BIG_INT;
+ if (has_legacy_octal)
+ goto fail;
+ } else {
+ if (is_float && radix != 10)
+ goto fail;
+ }
+ }
+ }
+
+ switch(atod_type) {
+ case ATOD_TYPE_FLOAT64:
+ {
+ double d;
+ d = js_strtod(buf, radix, is_float);
+ /* return int or float64 */
+ val = JS_NewFloat64(ctx, d);
+ }
+ break;
+ case ATOD_TYPE_BIG_INT:
+ if (has_legacy_octal || is_float)
+ goto fail;
+ val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);
+ break;
+ case ATOD_TYPE_BIG_FLOAT:
+ if (has_legacy_octal)
+ goto fail;
+ val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,
+ pexponent);
+ break;
+ case ATOD_TYPE_BIG_DECIMAL:
+ if (radix != 10)
+ goto fail;
+ val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);
+ break;
+ default:
+ abort();
+ }
+#else
+ {
+ double d;
+ (void)has_legacy_octal;
+ if (is_float && radix != 10)
+ goto fail;
+ d = js_strtod(buf, radix, is_float);
+ val = JS_NewFloat64(ctx, d);
+ }
+#endif
+
+done:
+ if (buf_allocated)
+ js_free_rt(ctx->rt, buf);
+ if (pp)
+ *pp = p;
+ return val;
+ fail:
+ val = JS_NAN;
+ goto done;
+ mem_error:
+ val = JS_ThrowOutOfMemory(ctx);
+ goto done;
+}
+
+#ifdef CONFIG_BIGNUM
+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ int radix, int flags)
+{
+ return js_atof2(ctx, str, pp, radix, flags, NULL);
+}
+#endif
+
+typedef enum JSToNumberHintEnum {
+ TON_FLAG_NUMBER,
+ TON_FLAG_NUMERIC,
+} JSToNumberHintEnum;
+
+static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
+ JSToNumberHintEnum flag)
+{
+ uint32_t tag;
+ JSValue ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_DECIMAL:
+ if (flag != TON_FLAG_NUMERIC) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");
+ }
+ ret = val;
+ break;
+ case JS_TAG_BIG_INT:
+ if (flag != TON_FLAG_NUMERIC) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
+ }
+ ret = val;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ if (flag != TON_FLAG_NUMERIC) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");
+ }
+ ret = val;
+ break;
+#endif
+ case JS_TAG_FLOAT64:
+ case JS_TAG_INT:
+ case JS_TAG_EXCEPTION:
+ ret = val;
+ break;
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_UNDEFINED:
+ ret = JS_NAN;
+ break;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ goto redo;
+ case JS_TAG_STRING:
+ {
+ const char *str;
+ const char *p;
+ size_t len;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ ret = JS_NewInt32(ctx, 0);
+ } else {
+ int flags = ATOD_ACCEPT_BIN_OCT;
+ ret = js_atof(ctx, p, &p, 0, flags);
+ if (!JS_IsException(ret)) {
+ p += skip_spaces(p);
+ if ((p - str) != len) {
+ JS_FreeValue(ctx, ret);
+ ret = JS_NAN;
+ }
+ }
+ }
+ JS_FreeCString(ctx, str);
+ }
+ break;
+ case JS_TAG_SYMBOL:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
+ default:
+ JS_FreeValue(ctx, val);
+ ret = JS_NAN;
+ break;
+ }
+ return ret;
+}
+
+static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
+{
+ return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
+}
+
+static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
+{
+ return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
+}
+
+static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
+}
+
+static warn_unused int JS_ToFloat64FreeImpl(JSContext *ctx, double *pres,
+ JSValue val)
+{
+ double d;
+ uint32_t tag;
+
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = JS_FLOAT64_NAN;
+ return -1;
+ }
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ d = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_FLOAT64:
+ d = JS_VALUE_GET_FLOAT64(val);
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ /* XXX: there can be a double rounding issue with some
+ primitives (such as JS_ToUint8ClampFree()), but it is
+ not critical to fix it. */
+ bf_get_float64(&p->num, &d, BF_RNDN);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ abort();
+ }
+ *pres = d;
+ return 0;
+}
+
+static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
+{
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_TAG(val);
+ if (tag <= JS_TAG_NULL) {
+ *pres = JS_VALUE_GET_INT(val);
+ return 0;
+ } else if (JS_TAG_IS_FLOAT64(tag)) {
+ *pres = JS_VALUE_GET_FLOAT64(val);
+ return 0;
+ } else {
+ return JS_ToFloat64FreeImpl(ctx, pres, val);
+ }
+}
+
+int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
+{
+ return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
+}
+
+static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
+}
+
+/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
+static maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
+{
+ uint32_t tag;
+ JSValue ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ ret = JS_NewInt32(ctx, 0);
+ } else {
+ /* convert -0 to +0 */
+ d = trunc(d) + 0.0;
+ ret = JS_NewFloat64(ctx, d);
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ bf_t a_s, *a, r_s, *r = &r_s;
+ BOOL is_nan;
+
+ a = JS_ToBigFloat(ctx, &a_s, val);
+ if (!bf_is_finite(a)) {
+ is_nan = bf_is_nan(a);
+ if (is_nan)
+ ret = JS_NewInt32(ctx, 0);
+ else
+ ret = JS_DupValue(ctx, val);
+ } else {
+ ret = JS_NewBigInt(ctx);
+ if (!JS_IsException(ret)) {
+ r = JS_GetBigInt(ret);
+ bf_set(r, a);
+ bf_rint(r, BF_RNDZ);
+ ret = JS_CompactBigInt(ctx, ret);
+ }
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return val;
+ goto redo;
+ }
+ return ret;
+}
+
+/* Note: the integer value is satured to 32 bits */
+static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
+{
+ uint32_t tag;
+ int ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_EXCEPTION:
+ *pres = 0;
+ return -1;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ ret = 0;
+ } else {
+ if (d < INT32_MIN)
+ ret = INT32_MIN;
+ else if (d > INT32_MAX)
+ ret = INT32_MAX;
+ else
+ ret = (int)d;
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_get_int32(&ret, &p->num, 0);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+ *pres = ret;
+ return 0;
+}
+
+int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
+{
+ return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
+}
+
+int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
+ int min, int max, int min_offset)
+{
+ int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
+ if (res == 0) {
+ if (*pres < min) {
+ *pres += min_offset;
+ if (*pres < min)
+ *pres = min;
+ } else {
+ if (*pres > max)
+ *pres = max;
+ }
+ }
+ return res;
+}
+
+static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
+{
+ uint32_t tag;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ *pres = JS_VALUE_GET_INT(val);
+ return 0;
+ case JS_TAG_EXCEPTION:
+ *pres = 0;
+ return -1;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ *pres = 0;
+ } else {
+ if (d < INT64_MIN)
+ *pres = INT64_MIN;
+ else if (d > INT64_MAX)
+ *pres = INT64_MAX;
+ else
+ *pres = (int64_t)d;
+ }
+ }
+ return 0;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_get_int64(pres, &p->num, 0);
+ JS_FreeValue(ctx, val);
+ }
+ return 0;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+}
+
+int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
+}
+
+int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
+ int64_t min, int64_t max, int64_t neg_offset)
+{
+ int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
+ if (res == 0) {
+ if (*pres < 0)
+ *pres += neg_offset;
+ if (*pres < min)
+ *pres = min;
+ else if (*pres > max)
+ *pres = max;
+ }
+ return res;
+}
+
+/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
+ in case of exception */
+static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
+{
+ uint32_t tag;
+ int64_t ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ double d;
+ int e;
+ d = JS_VALUE_GET_FLOAT64(val);
+ u.d = d;
+ /* we avoid doing fmod(x, 2^64) */
+ e = (u.u64 >> 52) & 0x7ff;
+ if (likely(e <= (1023 + 62))) {
+ /* fast case */
+ ret = (int64_t)d;
+ } else if (e <= (1023 + 62 + 53)) {
+ uint64_t v;
+ /* remainder modulo 2^64 */
+ v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
+ ret = v << ((e - 1023) - 52);
+ /* take the sign into account */
+ if (u.u64 >> 63)
+ ret = -ret;
+ } else {
+ ret = 0; /* also handles NaN and +inf */
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+ *pres = ret;
+ return 0;
+}
+
+int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
+}
+
+int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ if (JS_IsBigInt(ctx, val))
+ return JS_ToBigInt64(ctx, pres, val);
+ else
+ return JS_ToInt64(ctx, pres, val);
+}
+
+/* return (<0, 0) in case of exception */
+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
+{
+ uint32_t tag;
+ int32_t ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ double d;
+ int e;
+ d = JS_VALUE_GET_FLOAT64(val);
+ u.d = d;
+ /* we avoid doing fmod(x, 2^32) */
+ e = (u.u64 >> 52) & 0x7ff;
+ if (likely(e <= (1023 + 30))) {
+ /* fast case */
+ ret = (int32_t)d;
+ } else if (e <= (1023 + 30 + 53)) {
+ uint64_t v;
+ /* remainder modulo 2^32 */
+ v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
+ v = v << ((e - 1023) - 52 + 32);
+ ret = v >> 32;
+ /* take the sign into account */
+ if (u.u64 >> 63)
+ ret = -ret;
+ } else {
+ ret = 0; /* also handles NaN and +inf */
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+ *pres = ret;
+ return 0;
+}
+
+int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
+{
+ return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
+}
+
+static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
+{
+ return JS_ToInt32Free(ctx, (int32_t *)pres, val);
+}
+
+static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
+{
+ uint32_t tag;
+ int res;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ res = JS_VALUE_GET_INT(val);
+#ifdef CONFIG_BIGNUM
+ int_clamp:
+#endif
+ res = max_int(0, min_int(255, res));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ res = 0;
+ } else {
+ if (d < 0)
+ res = 0;
+ else if (d > 255)
+ res = 255;
+ else
+ res = lrint(d);
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_t r_s, *r = &r_s;
+ bf_init(ctx->bf_ctx, r);
+ bf_set(r, &p->num);
+ bf_rint(r, BF_RNDN);
+ bf_get_int32(&res, r, 0);
+ bf_delete(r);
+ JS_FreeValue(ctx, val);
+ }
+ goto int_clamp;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+ *pres = res;
+ return 0;
+}
+
+static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor)
+{
+ uint32_t tag, len;
+
+ tag = JS_VALUE_GET_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ {
+ int v;
+ v = JS_VALUE_GET_INT(val);
+ if (v < 0)
+ goto fail;
+ len = v;
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_t a;
+ BOOL res;
+ bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);
+ bf_init(ctx->bf_ctx, &a);
+ bf_set_ui(&a, len);
+ res = bf_cmp_eq(&a, &p->num);
+ bf_delete(&a);
+ JS_FreeValue(ctx, val);
+ if (!res)
+ goto fail;
+ }
+ break;
+#endif
+ default:
+ if (JS_TAG_IS_FLOAT64(tag)) {
+ double d;
+ d = JS_VALUE_GET_FLOAT64(val);
+ len = (uint32_t)d;
+ if (len != d)
+ goto fail;
+ } else {
+ uint32_t len1;
+
+ if (is_array_ctor) {
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ /* cannot recurse because val is a number */
+ if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
+ return -1;
+ } else {
+ /* legacy behavior: must do the conversion twice and compare */
+ if (JS_ToUint32(ctx, &len, val)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ /* cannot recurse because val is a number */
+ if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
+ return -1;
+ if (len1 != len) {
+ fail:
+ JS_ThrowRangeError(ctx, "invalid array length");
+ return -1;
+ }
+ }
+ }
+ break;
+ }
+ *plen = len;
+ return 0;
+}
+
+#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
+
+static BOOL is_safe_integer(double d)
+{
+ return isfinite(d) && floor(d) == d &&
+ fabs(d) <= (double)MAX_SAFE_INTEGER;
+}
+
+int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
+{
+ int64_t v;
+ if (JS_ToInt64Sat(ctx, &v, val))
+ return -1;
+ if (v < 0 || v > MAX_SAFE_INTEGER) {
+ JS_ThrowRangeError(ctx, "invalid array index");
+ *plen = 0;
+ return -1;
+ }
+ *plen = v;
+ return 0;
+}
+
+/* convert a value to a length between 0 and MAX_SAFE_INTEGER.
+ return -1 for exception */
+static warn_unused int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
+ JSValue val)
+{
+ int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
+ JS_FreeValue(ctx, val);
+ return res;
+}
+
+/* Note: can return an exception */
+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
+{
+ double d;
+ if (!JS_IsNumber(val))
+ return FALSE;
+ if (unlikely(JS_ToFloat64(ctx, &d, val)))
+ return -1;
+ return isfinite(d) && floor(d) == d;
+}
+
+static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
+{
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ {
+ int v;
+ v = JS_VALUE_GET_INT(val);
+ return (v < 0);
+ }
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ u.d = JS_VALUE_GET_FLOAT64(val);
+ return (u.u64 >> 63);
+ }
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ /* Note: integer zeros are not necessarily positive */
+ return p->num.sign && !bf_is_zero(&p->num);
+ }
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ return p->num.sign;
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ return p->num.sign;
+ }
+ break;
+#endif
+ default:
+ return FALSE;
+ }
+}
+
+#ifdef CONFIG_BIGNUM
+
+static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
+{
+ JSValue ret;
+ bf_t a_s, *a;
+ char *str;
+ int saved_sign;
+
+ a = JS_ToBigInt(ctx, &a_s, val);
+ if (!a)
+ return JS_EXCEPTION;
+ saved_sign = a->sign;
+ if (a->expn == BF_EXP_ZERO)
+ a->sign = 0;
+ str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |
+ BF_FTOA_JS_QUIRKS);
+ a->sign = saved_sign;
+ JS_FreeBigInt(ctx, a, &a_s);
+ if (!str)
+ return JS_ThrowOutOfMemory(ctx);
+ ret = JS_NewString(ctx, str);
+ bf_free(ctx->bf_ctx, str);
+ return ret;
+}
+
+static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
+{
+ return js_bigint_to_string1(ctx, val, 10);
+}
+
+static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,
+ limb_t prec, bf_flags_t flags)
+{
+ JSValue val, ret;
+ bf_t a_s, *a;
+ char *str;
+ int saved_sign;
+
+ val = JS_ToNumeric(ctx, val1);
+ if (JS_IsException(val))
+ return val;
+ a = JS_ToBigFloat(ctx, &a_s, val);
+ saved_sign = a->sign;
+ if (a->expn == BF_EXP_ZERO)
+ a->sign = 0;
+ flags |= BF_FTOA_JS_QUIRKS;
+ if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {
+ /* Note: for floating point numbers with a radix which is not
+ a power of two, the current precision is used to compute
+ the number of digits. */
+ if ((radix & (radix - 1)) != 0) {
+ bf_t r_s, *r = &r_s;
+ int prec, flags1;
+ /* must round first */
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
+ prec = ctx->fp_env.prec;
+ flags1 = ctx->fp_env.flags &
+ (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));
+ } else {
+ prec = 53;
+ flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;
+ }
+ bf_init(ctx->bf_ctx, r);
+ bf_set(r, a);
+ bf_round(r, prec, flags1 | BF_RNDN);
+ str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);
+ bf_delete(r);
+ } else {
+ str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);
+ }
+ } else {
+ str = bf_ftoa(NULL, a, radix, prec, flags);
+ }
+ a->sign = saved_sign;
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_ThrowOutOfMemory(ctx);
+ ret = JS_NewString(ctx, str);
+ bf_free(ctx->bf_ctx, str);
+ return ret;
+}
+
+static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)
+{
+ return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
+}
+
+static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,
+ limb_t prec, int flags)
+{
+ JSValue ret;
+ bfdec_t *a;
+ char *str;
+ int saved_sign;
+
+ a = JS_ToBigDecimal(ctx, val);
+ saved_sign = a->sign;
+ if (a->expn == BF_EXP_ZERO)
+ a->sign = 0;
+ str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
+ a->sign = saved_sign;
+ if (!str)
+ return JS_ThrowOutOfMemory(ctx);
+ ret = JS_NewString(ctx, str);
+ bf_free(ctx->bf_ctx, str);
+ return ret;
+}
+
+static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
+{
+ return js_bigdecimal_to_string1(ctx, val, 0,
+ BF_RNDZ | BF_FTOA_FORMAT_FREE);
+}
+
+#endif /* CONFIG_BIGNUM */
+
+/* 2 <= base <= 36 */
+static char *i64toa(char *buf_end, int64_t n, unsigned int base)
+{
+ char *q = buf_end;
+ int digit, is_neg;
+
+ is_neg = 0;
+ if (n < 0) {
+ is_neg = 1;
+ n = -n;
+ }
+ *--q = '\0';
+ do {
+ digit = (uint64_t)n % base;
+ n = (uint64_t)n / base;
+ if (digit < 10)
+ digit += '0';
+ else
+ digit += 'a' - 10;
+ *--q = digit;
+ } while (n != 0);
+ if (is_neg)
+ *--q = '-';
+ return q;
+}
+
+/* buf1 contains the printf result */
+static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
+ int rounding_mode, char *buf1, int buf1_size)
+{
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(rounding_mode);
+ snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(FE_TONEAREST);
+ *sign = (buf1[0] == '-');
+ /* mantissa */
+ buf[0] = buf1[1];
+ if (n_digits > 1)
+ memcpy(buf + 1, buf1 + 3, n_digits - 1);
+ buf[n_digits] = '\0';
+ /* exponent */
+ *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
+}
+
+/* maximum buffer size for js_dtoa */
+#define JS_DTOA_BUF_SIZE 128
+
+/* needed because ecvt usually limits the number of digits to
+ 17. Return the number of digits. */
+static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
+ BOOL is_fixed)
+{
+ int rounding_mode;
+ char buf_tmp[JS_DTOA_BUF_SIZE];
+
+ if (!is_fixed) {
+ unsigned int n_digits_min, n_digits_max;
+ /* find the minimum amount of digits (XXX: inefficient but simple) */
+ n_digits_min = 1;
+ n_digits_max = 17;
+ while (n_digits_min < n_digits_max) {
+ n_digits = (n_digits_min + n_digits_max) / 2;
+ js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
+ buf_tmp, sizeof(buf_tmp));
+ if (safe_strtod(buf_tmp, NULL) == d) {
+ /* no need to keep the trailing zeros */
+ while (n_digits >= 2 && buf[n_digits - 1] == '0')
+ n_digits--;
+ n_digits_max = n_digits;
+ } else {
+ n_digits_min = n_digits + 1;
+ }
+ }
+ n_digits = n_digits_max;
+ rounding_mode = FE_TONEAREST;
+ } else {
+ rounding_mode = FE_TONEAREST;
+#ifdef CONFIG_PRINTF_RNDN
+ {
+ char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
+ int decpt1, sign1, decpt2, sign2;
+ /* The JS rounding is specified as round to nearest ties away
+ from zero (RNDNA), but in printf the "ties" case is not
+ specified (for example it is RNDN for glibc, RNDNA for
+ Windows), so we must round manually. */
+ js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
+ buf_tmp, sizeof(buf_tmp));
+ /* XXX: could use 2 digits to reduce the average running time */
+ if (buf1[n_digits] == '5') {
+ js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
+ buf_tmp, sizeof(buf_tmp));
+ js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
+ buf_tmp, sizeof(buf_tmp));
+ if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
+ /* exact result: round away from zero */
+ if (sign1)
+ rounding_mode = FE_DOWNWARD;
+ else
+ rounding_mode = FE_UPWARD;
+ }
+ }
+ }
+#endif /* CONFIG_PRINTF_RNDN */
+ }
+ js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
+ buf_tmp, sizeof(buf_tmp));
+ return n_digits;
+}
+
+static int js_fcvt1(char *buf, int buf_size, double d, int n_digits,
+ int rounding_mode)
+{
+ int n;
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(rounding_mode);
+ n = snprintf(buf, buf_size, "%.*f", n_digits, d);
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(FE_TONEAREST);
+ assert(n < buf_size);
+ return n;
+}
+
+static void js_fcvt(char *buf, int buf_size, double d, int n_digits)
+{
+ int rounding_mode;
+ rounding_mode = FE_TONEAREST;
+#ifdef CONFIG_PRINTF_RNDN
+ {
+ int n1, n2;
+ char buf1[JS_DTOA_BUF_SIZE];
+ char buf2[JS_DTOA_BUF_SIZE];
+
+ /* The JS rounding is specified as round to nearest ties away from
+ zero (RNDNA), but in printf the "ties" case is not specified
+ (for example it is RNDN for glibc, RNDNA for Windows), so we
+ must round manually. */
+ n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);
+ rounding_mode = FE_TONEAREST;
+ /* XXX: could use 2 digits to reduce the average running time */
+ if (buf1[n1 - 1] == '5') {
+ n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);
+ n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);
+ if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
+ /* exact result: round away from zero */
+ if (buf1[0] == '-')
+ rounding_mode = FE_DOWNWARD;
+ else
+ rounding_mode = FE_UPWARD;
+ }
+ }
+ }
+#endif /* CONFIG_PRINTF_RNDN */
+ js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);
+}
+
+/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
+/* use as many digits as necessary */
+#define JS_DTOA_VAR_FORMAT (0 << 0)
+/* use n_digits significant digits (1 <= n_digits <= 101) */
+#define JS_DTOA_FIXED_FORMAT (1 << 0)
+/* force fractional format: [-]dd.dd with n_digits fractional digits */
+#define JS_DTOA_FRAC_FORMAT (2 << 0)
+/* force exponential notation either in fixed or variable format */
+#define JS_DTOA_FORCE_EXP (1 << 2)
+
+/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
+ XXX: radix != 10 is only supported for small integers
+*/
+static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)
+{
+ char *q;
+
+ if (!isfinite(d)) {
+ if (isnan(d)) {
+ strcpy(buf, "NaN");
+ } else {
+ q = buf;
+ if (d < 0)
+ *q++ = '-';
+ strcpy(q, "Infinity");
+ }
+ } else if (flags == JS_DTOA_VAR_FORMAT) {
+ int64_t i64;
+ char buf1[70], *ptr;
+ i64 = (int64_t)d;
+ if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)
+ goto generic_conv;
+ /* fast path for integers */
+ ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
+ strcpy(buf, ptr);
+ } else {
+ if (d == 0.0)
+ d = 0.0; /* convert -0 to 0 */
+ if (flags == JS_DTOA_FRAC_FORMAT) {
+ js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);
+ } else {
+ char buf1[JS_DTOA_BUF_SIZE];
+ int sign, decpt, k, n, i, p, n_max;
+ BOOL is_fixed;
+ generic_conv:
+ is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
+ if (is_fixed) {
+ n_max = n_digits;
+ } else {
+ n_max = 21;
+ }
+ /* the number has k digits (k >= 1) */
+ k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
+ n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
+ q = buf;
+ if (sign)
+ *q++ = '-';
+ if (flags & JS_DTOA_FORCE_EXP)
+ goto force_exp;
+ if (n >= 1 && n <= n_max) {
+ if (k <= n) {
+ memcpy(q, buf1, k);
+ q += k;
+ for(i = 0; i < (n - k); i++)
+ *q++ = '0';
+ *q = '\0';
+ } else {
+ /* k > n */
+ memcpy(q, buf1, n);
+ q += n;
+ *q++ = '.';
+ for(i = 0; i < (k - n); i++)
+ *q++ = buf1[n + i];
+ *q = '\0';
+ }
+ } else if (n >= -5 && n <= 0) {
+ *q++ = '0';
+ *q++ = '.';
+ for(i = 0; i < -n; i++)
+ *q++ = '0';
+ memcpy(q, buf1, k);
+ q += k;
+ *q = '\0';
+ } else {
+ force_exp:
+ /* exponential notation */
+ *q++ = buf1[0];
+ if (k > 1) {
+ *q++ = '.';
+ for(i = 1; i < k; i++)
+ *q++ = buf1[i];
+ }
+ *q++ = 'e';
+ p = n - 1;
+ if (p >= 0)
+ *q++ = '+';
+ sprintf(q, "%d", p);
+ }
+ }
+ }
+}
+
+static JSValue js_dtoa(JSContext *ctx,
+ double d, int radix, int n_digits, int flags)
+{
+ char buf[JS_DTOA_BUF_SIZE];
+ js_dtoa1(buf, d, radix, n_digits, flags);
+ return JS_NewString(ctx, buf);
+}
+
+JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
+{
+ uint32_t tag;
+ const char *str;
+ char buf[32];
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_STRING:
+ return JS_DupValue(ctx, val);
+ case JS_TAG_INT:
+ snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
+ str = buf;
+ goto new_string;
+ case JS_TAG_BOOL:
+ return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
+ JS_ATOM_true : JS_ATOM_false);
+ case JS_TAG_NULL:
+ return JS_AtomToString(ctx, JS_ATOM_null);
+ case JS_TAG_UNDEFINED:
+ return JS_AtomToString(ctx, JS_ATOM_undefined);
+ case JS_TAG_EXCEPTION:
+ return JS_EXCEPTION;
+ case JS_TAG_OBJECT:
+ {
+ JSValue val1, ret;
+ val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
+ if (JS_IsException(val1))
+ return val1;
+ ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
+ JS_FreeValue(ctx, val1);
+ return ret;
+ }
+ break;
+ case JS_TAG_FUNCTION_BYTECODE:
+ str = "[function bytecode]";
+ goto new_string;
+ case JS_TAG_SYMBOL:
+ if (is_ToPropertyKey) {
+ return JS_DupValue(ctx, val);
+ } else {
+ return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
+ }
+ case JS_TAG_FLOAT64:
+ return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
+ JS_DTOA_VAR_FORMAT);
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ return ctx->rt->bigint_ops.to_string(ctx, val);
+ case JS_TAG_BIG_FLOAT:
+ return ctx->rt->bigfloat_ops.to_string(ctx, val);
+ case JS_TAG_BIG_DECIMAL:
+ return ctx->rt->bigdecimal_ops.to_string(ctx, val);
+#endif
+ default:
+ str = "[unsupported type]";
+ new_string:
+ return JS_NewString(ctx, str);
+ }
+}
+
+JSValue JS_ToString(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToStringInternal(ctx, val, FALSE);
+}
+
+static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
+{
+ JSValue ret;
+ ret = JS_ToString(ctx, val);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
+{
+ if (JS_IsUndefined(val) || JS_IsNull(val))
+ return JS_ToStringFree(ctx, val);
+ return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
+}
+
+JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToStringInternal(ctx, val, TRUE);
+}
+
+static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(val);
+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
+ return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
+ return JS_ToString(ctx, val);
+}
+
+static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
+{
+ JSValue val;
+ JSString *p;
+ int i;
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+ char buf[16];
+
+ val = JS_ToStringCheckObject(ctx, val1);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+
+ if (string_buffer_init(ctx, b, p->len + 2))
+ goto fail;
+
+ if (string_buffer_putc8(b, '\"'))
+ goto fail;
+ for(i = 0; i < p->len; ) {
+ c = string_getc(p, &i);
+ switch(c) {
+ case '\t':
+ c = 't';
+ goto quote;
+ case '\r':
+ c = 'r';
+ goto quote;
+ case '\n':
+ c = 'n';
+ goto quote;
+ case '\b':
+ c = 'b';
+ goto quote;
+ case '\f':
+ c = 'f';
+ goto quote;
+ case '\"':
+ case '\\':
+ quote:
+ if (string_buffer_putc8(b, '\\'))
+ goto fail;
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ break;
+ default:
+ if (c < 32 || (c >= 0xd800 && c < 0xe000)) {
+ snprintf(buf, sizeof(buf), "\\u%04x", c);
+ if (string_buffer_puts8(b, buf))
+ goto fail;
+ } else {
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ break;
+ }
+ }
+ if (string_buffer_putc8(b, '\"'))
+ goto fail;
+ JS_FreeValue(ctx, val);
+ return string_buffer_end(b);
+ fail:
+ JS_FreeValue(ctx, val);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
+{
+ printf("%14s %4s %4s %14s %10s %s\n",
+ "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
+}
+
+/* for debug only: dump an object without side effect */
+static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+{
+ uint32_t i;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ JSShape *sh;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ BOOL is_first = TRUE;
+
+ /* XXX: should encode atoms with special characters */
+ sh = p->shape; /* the shape can be NULL while freeing an object */
+ printf("%14p %4d ",
+ (void *)p,
+ p->header.ref_count);
+ if (sh) {
+ printf("%3d%c %14p ",
+ sh->header.ref_count,
+ " *"[sh->is_hashed],
+ (void *)sh->proto);
+ } else {
+ printf("%3s %14s ", "-", "-");
+ }
+ printf("%10s ",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
+ if (p->is_exotic && p->fast_array) {
+ printf("[ ");
+ for(i = 0; i < p->u.array.count; i++) {
+ if (i != 0)
+ printf(", ");
+ switch (p->class_id) {
+ case JS_CLASS_ARRAY:
+ case JS_CLASS_ARGUMENTS:
+ JS_DumpValueShort(rt, p->u.array.u.values[i]);
+ break;
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ case JS_CLASS_FLOAT64_ARRAY:
+ {
+ int size = 1 << typed_array_size_log2(p->class_id);
+ const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
+ while (size-- > 0)
+ printf("%02X", *b++);
+ }
+ break;
+ }
+ }
+ printf(" ] ");
+ }
+
+ if (sh) {
+ printf("{ ");
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ if (prs->atom != JS_ATOM_NULL) {
+ pr = &p->prop[i];
+ if (!is_first)
+ printf(", ");
+ printf("%s: ",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ printf("[getset %p %p]", (void *)pr->u.getset.getter,
+ (void *)pr->u.getset.setter);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ printf("[varref %p]", (void *)pr->u.var_ref);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ printf("[autoinit %p %d %p]",
+ (void *)js_autoinit_get_realm(pr),
+ js_autoinit_get_id(pr),
+ pr->u.init.opaque);
+ } else {
+ JS_DumpValueShort(rt, pr->u.value);
+ }
+ is_first = FALSE;
+ }
+ }
+ printf(" }");
+ }
+
+ if (js_class_has_bytecode(p->class_id)) {
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ JSVarRef **var_refs;
+ if (b->closure_var_count) {
+ var_refs = p->u.func.var_refs;
+ printf(" Closure:");
+ for(i = 0; i < b->closure_var_count; i++) {
+ printf(" ");
+ JS_DumpValueShort(rt, var_refs[i]->value);
+ }
+ if (p->u.func.home_object) {
+ printf(" HomeObject: ");
+ JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
+ }
+ }
+ }
+ printf("\n");
+}
+
+static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+{
+ if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ JS_DumpObject(rt, (JSObject *)p);
+ } else {
+ printf("%14p %4d ",
+ (void *)p,
+ p->ref_count);
+ switch(p->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+ printf("[function bytecode]");
+ break;
+ case JS_GC_OBJ_TYPE_SHAPE:
+ printf("[shape]");
+ break;
+ case JS_GC_OBJ_TYPE_VAR_REF:
+ printf("[var_ref]");
+ break;
+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
+ printf("[async_function]");
+ break;
+ case JS_GC_OBJ_TYPE_JS_CONTEXT:
+ printf("[js_context]");
+ break;
+ default:
+ printf("[unknown %d]", p->gc_obj_type);
+ break;
+ }
+ printf("\n");
+ }
+}
+
+static maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val)
+{
+ uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
+ const char *str;
+
+ switch(tag) {
+ case JS_TAG_INT:
+ printf("%d", JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_BOOL:
+ if (JS_VALUE_GET_BOOL(val))
+ str = "true";
+ else
+ str = "false";
+ goto print_str;
+ case JS_TAG_NULL:
+ str = "null";
+ goto print_str;
+ case JS_TAG_EXCEPTION:
+ str = "exception";
+ goto print_str;
+ case JS_TAG_UNINITIALIZED:
+ str = "uninitialized";
+ goto print_str;
+ case JS_TAG_UNDEFINED:
+ str = "undefined";
+ print_str:
+ printf("%s", str);
+ break;
+ case JS_TAG_FLOAT64:
+ printf("%.14g", JS_VALUE_GET_FLOAT64(val));
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ char *str;
+ str = bf_ftoa(NULL, &p->num, 10, 0,
+ BF_RNDZ | BF_FTOA_FORMAT_FRAC);
+ printf("%sn", str);
+ bf_realloc(&rt->bf_ctx, str, 0);
+ }
+ break;
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ char *str;
+ str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,
+ BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);
+ printf("%sl", str);
+ bf_free(&rt->bf_ctx, str);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ char *str;
+ str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,
+ BF_RNDZ | BF_FTOA_FORMAT_FREE);
+ printf("%sm", str);
+ bf_free(&rt->bf_ctx, str);
+ }
+ break;
+#endif
+ case JS_TAG_STRING:
+ {
+ JSString *p;
+ p = JS_VALUE_GET_STRING(val);
+ JS_DumpString(rt, p);
+ }
+ break;
+ case JS_TAG_FUNCTION_BYTECODE:
+ {
+ JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
+ }
+ break;
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSAtom atom = rt->class_array[p->class_id].class_name;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ printf("[%s %p]",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
+ }
+ break;
+ case JS_TAG_SYMBOL:
+ {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ printf("Symbol(%s)",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
+ }
+ break;
+ case JS_TAG_MODULE:
+ printf("[module]");
+ break;
+ default:
+ printf("[unknown tag %d]", tag);
+ break;
+ }
+}
+
+static maybe_unused void JS_DumpValue(JSContext *ctx,
+ JSValueConst val)
+{
+ JS_DumpValueShort(ctx->rt, val);
+}
+
+static maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val)
+{
+ printf("%s=", str);
+ JS_DumpValueShort(ctx->rt, val);
+ printf("\n");
+}
+
+/* return -1 if exception (proxy case) or TRUE/FALSE */
+int JS_IsArray(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(val);
+ if (unlikely(p->class_id == JS_CLASS_PROXY))
+ return js_proxy_isArray(ctx, val);
+ else
+ return p->class_id == JS_CLASS_ARRAY;
+ } else {
+ return FALSE;
+ }
+}
+
+static double js_pow(double a, double b)
+{
+ if (unlikely(!isfinite(b)) && fabs(a) == 1) {
+ /* not compatible with IEEE 754 */
+ return JS_FLOAT64_NAN;
+ } else {
+ return pow(a, b);
+ }
+}
+
+#ifdef CONFIG_BIGNUM
+
+JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
+{
+ JSValue val;
+ bf_t *a;
+ val = JS_NewBigInt(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigInt(val);
+ if (bf_set_si(a, v)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ return val;
+}
+
+JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
+{
+ if (is_math_mode(ctx) &&
+ v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
+ return JS_NewInt64(ctx, v);
+ } else {
+ return JS_NewBigInt64_1(ctx, v);
+ }
+}
+
+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
+{
+ JSValue val;
+ if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
+ val = JS_NewInt64(ctx, v);
+ } else {
+ bf_t *a;
+ val = JS_NewBigInt(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigInt(val);
+ if (bf_set_ui(a, v)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ }
+ return val;
+}
+
+/* if the returned bigfloat is allocated it is equal to
+ 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return
+ NULL in case of error. */
+static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)
+{
+ uint32_t tag;
+ bf_t *r;
+ JSBigFloat *p;
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ if (bf_set_si(r, JS_VALUE_GET_INT(val)))
+ goto fail;
+ break;
+ case JS_TAG_FLOAT64:
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {
+ fail:
+ bf_delete(r);
+ return NULL;
+ }
+ break;
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ p = JS_VALUE_GET_PTR(val);
+ r = &p->num;
+ break;
+ case JS_TAG_UNDEFINED:
+ default:
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ bf_set_nan(r);
+ break;
+ }
+ return r;
+}
+
+/* return NULL if invalid type */
+static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
+{
+ uint32_t tag;
+ JSBigDecimal *p;
+ bfdec_t *r;
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_BIG_DECIMAL:
+ p = JS_VALUE_GET_PTR(val);
+ r = &p->num;
+ break;
+ default:
+ JS_ThrowTypeError(ctx, "bigdecimal expected");
+ r = NULL;
+ break;
+ }
+ return r;
+}
+
+/* return NaN if bad bigint literal */
+static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
+{
+ const char *str, *p;
+ size_t len;
+ int flags;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ val = JS_NewBigInt64(ctx, 0);
+ } else {
+ flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
+ if (is_math_mode(ctx))
+ flags |= ATOD_MODE_BIGINT;
+ val = js_atof(ctx, p, &p, 0, flags);
+ p += skip_spaces(p);
+ if (!JS_IsException(val)) {
+ if ((p - str) != len) {
+ JS_FreeValue(ctx, val);
+ val = JS_NAN;
+ }
+ }
+ }
+ JS_FreeCString(ctx, str);
+ return val;
+}
+
+static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
+{
+ val = JS_StringToBigInt(ctx, val);
+ if (JS_VALUE_IS_NAN(val))
+ return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
+ return val;
+}
+
+/* if the returned bigfloat is allocated it is equal to
+ 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
+static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
+{
+ uint32_t tag;
+ bf_t *r;
+ JSBigFloat *p;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ if (!is_math_mode(ctx))
+ goto fail;
+ /* fall tru */
+ case JS_TAG_BOOL:
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ bf_set_si(r, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (!is_math_mode(ctx))
+ goto fail;
+ if (!isfinite(d))
+ goto fail;
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ d = trunc(d);
+ bf_set_float64(r, d);
+ }
+ break;
+ case JS_TAG_BIG_INT:
+ p = JS_VALUE_GET_PTR(val);
+ r = &p->num;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ if (!is_math_mode(ctx))
+ goto fail;
+ p = JS_VALUE_GET_PTR(val);
+ if (!bf_is_finite(&p->num))
+ goto fail;
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ bf_set(r, &p->num);
+ bf_rint(r, BF_RNDZ);
+ JS_FreeValue(ctx, val);
+ break;
+ case JS_TAG_STRING:
+ val = JS_StringToBigIntErr(ctx, val);
+ if (JS_IsException(val))
+ return NULL;
+ goto redo;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ return NULL;
+ goto redo;
+ default:
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeError(ctx, "cannot convert to bigint");
+ return NULL;
+ }
+ return r;
+}
+
+static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
+{
+ return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));
+}
+
+static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)
+{
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {
+ return val;
+ } else {
+ bf_t a_s, *a, *r;
+ int ret;
+ JSValue res;
+
+ res = JS_NewBigInt(ctx);
+ if (JS_IsException(res))
+ return JS_EXCEPTION;
+ a = JS_ToBigIntFree(ctx, &a_s, val);
+ if (!a) {
+ JS_FreeValue(ctx, res);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigInt(res);
+ ret = bf_set(r, a);
+ JS_FreeBigInt(ctx, a, &a_s);
+ if (ret) {
+ JS_FreeValue(ctx, res);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ return JS_CompactBigInt(ctx, res);
+ }
+}
+
+/* free the bf_t allocated by JS_ToBigInt */
+static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
+{
+ if (a == buf) {
+ bf_delete(a);
+ } else {
+ JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
+ offsetof(JSBigFloat, num));
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));
+ }
+}
+
+/* XXX: merge with JS_ToInt64Free with a specific flag */
+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
+{
+ bf_t a_s, *a;
+
+ a = JS_ToBigIntFree(ctx, &a_s, val);
+ if (!a) {
+ *pres = 0;
+ return -1;
+ }
+ bf_get_int64(pres, a, BF_GET_INT_MOD);
+ JS_FreeBigInt(ctx, a, &a_s);
+ return 0;
+}
+
+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
+}
+
+static JSBigFloat *js_new_bf(JSContext *ctx)
+{
+ JSBigFloat *p;
+ p = js_malloc(ctx, sizeof(*p));
+ if (!p)
+ return NULL;
+ p->header.ref_count = 1;
+ bf_init(ctx->bf_ctx, &p->num);
+ return p;
+}
+
+static JSValue JS_NewBigFloat(JSContext *ctx)
+{
+ JSBigFloat *p;
+ p = js_malloc(ctx, sizeof(*p));
+ if (!p)
+ return JS_EXCEPTION;
+ p->header.ref_count = 1;
+ bf_init(ctx->bf_ctx, &p->num);
+ return JS_MKPTR(JS_TAG_BIG_FLOAT, p);
+}
+
+static JSValue JS_NewBigDecimal(JSContext *ctx)
+{
+ JSBigDecimal *p;
+ p = js_malloc(ctx, sizeof(*p));
+ if (!p)
+ return JS_EXCEPTION;
+ p->header.ref_count = 1;
+ bfdec_init(ctx->bf_ctx, &p->num);
+ return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
+}
+
+static JSValue JS_NewBigInt(JSContext *ctx)
+{
+ JSBigFloat *p;
+ p = js_malloc(ctx, sizeof(*p));
+ if (!p)
+ return JS_EXCEPTION;
+ p->header.ref_count = 1;
+ bf_init(ctx->bf_ctx, &p->num);
+ return JS_MKPTR(JS_TAG_BIG_INT, p);
+}
+
+static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
+ BOOL convert_to_safe_integer)
+{
+ int64_t v;
+ bf_t *a;
+
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
+ return val; /* fail safe */
+ a = JS_GetBigInt(val);
+ if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
+ v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
+ JS_FreeValue(ctx, val);
+ return JS_NewInt64(ctx, v);
+ } else if (a->expn == BF_EXP_ZERO && a->sign) {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ assert(p->header.ref_count == 1);
+ a->sign = 0;
+ }
+ return val;
+}
+
+/* Convert the big int to a safe integer if in math mode. normalize
+ the zero representation. Could also be used to convert the bigint
+ to a short bigint value. The reference count of the value must be
+ 1. Cannot fail */
+static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
+{
+ return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
+}
+
+/* must be kept in sync with JSOverloadableOperatorEnum */
+/* XXX: use atoms ? */
+static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {
+ "+",
+ "-",
+ "*",
+ "/",
+ "%",
+ "**",
+ "|",
+ "&",
+ "^",
+ "<<",
+ ">>",
+ ">>>",
+ "==",
+ "<",
+ "pos",
+ "neg",
+ "++",
+ "--",
+ "~",
+};
+
+static int get_ovop_from_opcode(OPCodeEnum op)
+{
+ switch(op) {
+ case OP_add:
+ return JS_OVOP_ADD;
+ case OP_sub:
+ return JS_OVOP_SUB;
+ case OP_mul:
+ return JS_OVOP_MUL;
+ case OP_div:
+ return JS_OVOP_DIV;
+ case OP_mod:
+ case OP_math_mod:
+ return JS_OVOP_MOD;
+ case OP_pow:
+ return JS_OVOP_POW;
+ case OP_or:
+ return JS_OVOP_OR;
+ case OP_and:
+ return JS_OVOP_AND;
+ case OP_xor:
+ return JS_OVOP_XOR;
+ case OP_shl:
+ return JS_OVOP_SHL;
+ case OP_sar:
+ return JS_OVOP_SAR;
+ case OP_shr:
+ return JS_OVOP_SHR;
+ case OP_eq:
+ case OP_neq:
+ return JS_OVOP_EQ;
+ case OP_lt:
+ case OP_lte:
+ case OP_gt:
+ case OP_gte:
+ return JS_OVOP_LESS;
+ case OP_plus:
+ return JS_OVOP_POS;
+ case OP_neg:
+ return JS_OVOP_NEG;
+ case OP_inc:
+ return JS_OVOP_INC;
+ case OP_dec:
+ return JS_OVOP_DEC;
+ default:
+ abort();
+ }
+}
+
+/* return NULL if not present */
+static JSObject *find_binary_op(JSBinaryOperatorDef *def,
+ uint32_t operator_index,
+ JSOverloadableOperatorEnum op)
+{
+ JSBinaryOperatorDefEntry *ent;
+ int i;
+ for(i = 0; i < def->count; i++) {
+ ent = &def->tab[i];
+ if (ent->operator_index == operator_index)
+ return ent->ops[op];
+ }
+ return NULL;
+}
+
+/* return -1 if exception, 0 if no operator overloading, 1 if
+ overloaded operator called */
+static WARN_UNUSED int js_call_binary_op_fallback(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst op1,
+ JSValueConst op2,
+ OPCodeEnum op,
+ BOOL is_numeric,
+ int hint)
+{
+ JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;
+ JSOperatorSetData *opset1, *opset2;
+ JSOverloadableOperatorEnum ovop;
+ JSObject *p;
+ JSValueConst args[2];
+
+ if (!ctx->allow_operator_overloading)
+ return 0;
+
+ opset2_obj = JS_UNDEFINED;
+ opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset1_obj))
+ goto exception;
+ if (JS_IsUndefined(opset1_obj))
+ return 0;
+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset1)
+ goto exception;
+
+ opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset2_obj))
+ goto exception;
+ if (JS_IsUndefined(opset2_obj)) {
+ JS_FreeValue(ctx, opset1_obj);
+ return 0;
+ }
+ opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset2)
+ goto exception;
+
+ if (opset1->is_primitive && opset2->is_primitive) {
+ JS_FreeValue(ctx, opset1_obj);
+ JS_FreeValue(ctx, opset2_obj);
+ return 0;
+ }
+
+ ovop = get_ovop_from_opcode(op);
+
+ if (opset1->operator_counter == opset2->operator_counter) {
+ p = opset1->self_ops[ovop];
+ } else if (opset1->operator_counter > opset2->operator_counter) {
+ p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);
+ } else {
+ p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);
+ }
+ if (!p) {
+ JS_ThrowTypeError(ctx, "operator %s: no function defined",
+ js_overloadable_operator_names[ovop]);
+ goto exception;
+ }
+
+ if (opset1->is_primitive) {
+ if (is_numeric) {
+ new_op1 = JS_ToNumeric(ctx, op1);
+ } else {
+ new_op1 = JS_ToPrimitive(ctx, op1, hint);
+ }
+ if (JS_IsException(new_op1))
+ goto exception;
+ } else {
+ new_op1 = JS_DupValue(ctx, op1);
+ }
+
+ if (opset2->is_primitive) {
+ if (is_numeric) {
+ new_op2 = JS_ToNumeric(ctx, op2);
+ } else {
+ new_op2 = JS_ToPrimitive(ctx, op2, hint);
+ }
+ if (JS_IsException(new_op2)) {
+ JS_FreeValue(ctx, new_op1);
+ goto exception;
+ }
+ } else {
+ new_op2 = JS_DupValue(ctx, op2);
+ }
+
+ /* XXX: could apply JS_ToPrimitive() if primitive type so that the
+ operator function does not get a value object */
+
+ method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {
+ args[0] = new_op2;
+ args[1] = new_op1;
+ } else {
+ args[0] = new_op1;
+ args[1] = new_op2;
+ }
+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
+ JS_FreeValue(ctx, new_op1);
+ JS_FreeValue(ctx, new_op2);
+ if (JS_IsException(ret))
+ goto exception;
+ if (ovop == JS_OVOP_EQ) {
+ BOOL res = JS_ToBoolFree(ctx, ret);
+ if (op == OP_neq)
+ res ^= 1;
+ ret = JS_NewBool(ctx, res);
+ } else if (ovop == JS_OVOP_LESS) {
+ if (JS_IsUndefined(ret)) {
+ ret = JS_FALSE;
+ } else {
+ BOOL res = JS_ToBoolFree(ctx, ret);
+ if (op == OP_lte || op == OP_gte)
+ res ^= 1;
+ ret = JS_NewBool(ctx, res);
+ }
+ }
+ JS_FreeValue(ctx, opset1_obj);
+ JS_FreeValue(ctx, opset2_obj);
+ *pret = ret;
+ return 1;
+ exception:
+ JS_FreeValue(ctx, opset1_obj);
+ JS_FreeValue(ctx, opset2_obj);
+ *pret = JS_UNDEFINED;
+ return -1;
+}
+
+/* try to call the operation on the operatorSet field of 'obj'. Only
+ used for "/" and "**" on the BigInt prototype in math mode */
+static WARN_UNUSED int js_call_binary_op_simple(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst obj,
+ JSValueConst op1,
+ JSValueConst op2,
+ OPCodeEnum op)
+{
+ JSValue opset1_obj, method, ret, new_op1, new_op2;
+ JSOperatorSetData *opset1;
+ JSOverloadableOperatorEnum ovop;
+ JSObject *p;
+ JSValueConst args[2];
+
+ opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset1_obj))
+ goto exception;
+ if (JS_IsUndefined(opset1_obj))
+ return 0;
+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset1)
+ goto exception;
+ ovop = get_ovop_from_opcode(op);
+
+ p = opset1->self_ops[ovop];
+ if (!p) {
+ JS_FreeValue(ctx, opset1_obj);
+ return 0;
+ }
+
+ new_op1 = JS_ToNumeric(ctx, op1);
+ if (JS_IsException(new_op1))
+ goto exception;
+ new_op2 = JS_ToNumeric(ctx, op2);
+ if (JS_IsException(new_op2)) {
+ JS_FreeValue(ctx, new_op1);
+ goto exception;
+ }
+
+ method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ args[0] = new_op1;
+ args[1] = new_op2;
+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
+ JS_FreeValue(ctx, new_op1);
+ JS_FreeValue(ctx, new_op2);
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, opset1_obj);
+ *pret = ret;
+ return 1;
+ exception:
+ JS_FreeValue(ctx, opset1_obj);
+ *pret = JS_UNDEFINED;
+ return -1;
+}
+
+/* return -1 if exception, 0 if no operator overloading, 1 if
+ overloaded operator called */
+static WARN_UNUSED int js_call_unary_op_fallback(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst op1,
+ OPCodeEnum op)
+{
+ JSValue opset1_obj, method, ret;
+ JSOperatorSetData *opset1;
+ JSOverloadableOperatorEnum ovop;
+ JSObject *p;
+
+ if (!ctx->allow_operator_overloading)
+ return 0;
+
+ opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset1_obj))
+ goto exception;
+ if (JS_IsUndefined(opset1_obj))
+ return 0;
+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset1)
+ goto exception;
+ if (opset1->is_primitive) {
+ JS_FreeValue(ctx, opset1_obj);
+ return 0;
+ }
+
+ ovop = get_ovop_from_opcode(op);
+
+ p = opset1->self_ops[ovop];
+ if (!p) {
+ JS_ThrowTypeError(ctx, "no overloaded operator %s",
+ js_overloadable_operator_names[ovop]);
+ goto exception;
+ }
+ method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, opset1_obj);
+ *pret = ret;
+ return 1;
+ exception:
+ JS_FreeValue(ctx, opset1_obj);
+ *pret = JS_UNDEFINED;
+ return -1;
+}
+
+static JSValue throw_bf_exception(JSContext *ctx, int status)
+{
+ const char *str;
+ if (status & BF_ST_MEM_ERROR)
+ return JS_ThrowOutOfMemory(ctx);
+ if (status & BF_ST_DIVIDE_ZERO) {
+ str = "division by zero";
+ } else if (status & BF_ST_INVALID_OP) {
+ str = "invalid operation";
+ } else {
+ str = "integer overflow";
+ }
+ return JS_ThrowRangeError(ctx, "%s", str);
+}
+
+static int js_unary_arith_bigint(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1)
+{
+ bf_t a_s, *r, *a;
+ int ret, v;
+ JSValue res;
+
+ if (op == OP_plus && !is_math_mode(ctx)) {
+ JS_ThrowTypeError(ctx, "bigint argument with unary +");
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ res = JS_NewBigInt(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ r = JS_GetBigInt(res);
+ a = JS_ToBigInt(ctx, &a_s, op1);
+ ret = 0;
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_plus:
+ ret = bf_set(r, a);
+ break;
+ case OP_neg:
+ ret = bf_set(r, a);
+ bf_neg(r);
+ break;
+ case OP_not:
+ ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);
+ bf_neg(r);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeValue(ctx, op1);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ res = JS_CompactBigInt(ctx, res);
+ *pres = res;
+ return 0;
+}
+
+static int js_unary_arith_bigfloat(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1)
+{
+ bf_t a_s, *r, *a;
+ int ret, v;
+ JSValue res;
+
+ if (op == OP_plus && !is_math_mode(ctx)) {
+ JS_ThrowTypeError(ctx, "bigfloat argument with unary +");
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ r = JS_GetBigFloat(res);
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ ret = 0;
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_plus:
+ ret = bf_set(r, a);
+ break;
+ case OP_neg:
+ ret = bf_set(r, a);
+ bf_neg(r);
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ if (unlikely(ret & BF_ST_MEM_ERROR)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+}
+
+static int js_unary_arith_bigdecimal(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1)
+{
+ bfdec_t *r, *a;
+ int ret, v;
+ JSValue res;
+
+ if (op == OP_plus && !is_math_mode(ctx)) {
+ JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+
+ res = JS_NewBigDecimal(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ r = JS_GetBigDecimal(res);
+ a = JS_ToBigDecimal(ctx, op1);
+ ret = 0;
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_plus:
+ ret = bfdec_set(r, a);
+ break;
+ case OP_neg:
+ ret = bfdec_set(r, a);
+ bfdec_neg(r);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+}
+
+static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, val;
+ int v, ret;
+ uint32_t tag;
+
+ op1 = sp[-1];
+ /* fast path for float64 */
+ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
+ goto handle_float64;
+ if (JS_IsObject(op1)) {
+ ret = js_call_unary_op_fallback(ctx, &val, op1, op);
+ if (ret < 0)
+ return -1;
+ if (ret) {
+ JS_FreeValue(ctx, op1);
+ sp[-1] = val;
+ return 0;
+ }
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1))
+ goto exception;
+ tag = JS_VALUE_GET_TAG(op1);
+ switch(tag) {
+ case JS_TAG_INT:
+ {
+ int64_t v64;
+ v64 = JS_VALUE_GET_INT(op1);
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ v64 += v;
+ break;
+ case OP_plus:
+ break;
+ case OP_neg:
+ if (v64 == 0) {
+ sp[-1] = __JS_NewFloat64(ctx, -0.0);
+ return 0;
+ } else {
+ v64 = -v64;
+ }
+ break;
+ default:
+ abort();
+ }
+ sp[-1] = JS_NewInt64(ctx, v64);
+ }
+ break;
+ case JS_TAG_BIG_INT:
+ handle_bigint:
+ if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
+ goto exception;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))
+ goto exception;
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))
+ goto exception;
+ break;
+ default:
+ handle_float64:
+ {
+ double d;
+ if (is_math_mode(ctx))
+ goto handle_bigint;
+ d = JS_VALUE_GET_FLOAT64(op1);
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ d += v;
+ break;
+ case OP_plus:
+ break;
+ case OP_neg:
+ d = -d;
+ break;
+ default:
+ abort();
+ }
+ sp[-1] = __JS_NewFloat64(ctx, d);
+ }
+ break;
+ }
+ return 0;
+ exception:
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static WARN_UNUSED int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+{
+ JSValue op1;
+
+ /* XXX: allow custom operators */
+ op1 = sp[-1];
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+ }
+ sp[-1] = op1;
+ sp[0] = JS_DupValue(ctx, op1);
+ return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
+}
+
+static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, val;
+ int ret;
+
+ op1 = sp[-1];
+ if (JS_IsObject(op1)) {
+ ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);
+ if (ret < 0)
+ return -1;
+ if (ret) {
+ JS_FreeValue(ctx, op1);
+ sp[-1] = val;
+ return 0;
+ }
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1))
+ goto exception;
+ if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
+ if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
+ goto exception;
+ } else {
+ int32_t v1;
+ if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
+ goto exception;
+ sp[-1] = JS_NewInt32(ctx, ~v1);
+ }
+ return 0;
+ exception:
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2)
+{
+ bf_t a_s, b_s, *r, *a, *b;
+ int ret;
+ JSValue res;
+
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return -1;
+ }
+ r = JS_GetBigFloat(res);
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ b = JS_ToBigFloat(ctx, &b_s, op2);
+ bf_init(ctx->bf_ctx, r);
+ switch(op) {
+ case OP_add:
+ ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_sub:
+ ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_mul:
+ ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_div:
+ ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_math_mod:
+ /* Euclidian remainder */
+ ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
+ BF_DIVREM_EUCLIDIAN);
+ break;
+ case OP_mod:
+ ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
+ BF_RNDZ);
+ break;
+ case OP_pow:
+ ret = bf_pow(r, a, b, ctx->fp_env.prec,
+ ctx->fp_env.flags | BF_POW_JS_QUIRKS);
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (unlikely(ret & BF_ST_MEM_ERROR)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+}
+
+static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2)
+{
+ bf_t a_s, b_s, *r, *a, *b;
+ int ret;
+ JSValue res;
+
+ res = JS_NewBigInt(ctx);
+ if (JS_IsException(res))
+ goto fail;
+ a = JS_ToBigInt(ctx, &a_s, op1);
+ if (!a)
+ goto fail;
+ b = JS_ToBigInt(ctx, &b_s, op2);
+ if (!b) {
+ JS_FreeBigInt(ctx, a, &a_s);
+ goto fail;
+ }
+ r = JS_GetBigInt(res);
+ ret = 0;
+ switch(op) {
+ case OP_add:
+ ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_sub:
+ ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_mul:
+ ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_div:
+ if (!is_math_mode(ctx)) {
+ bf_t rem_s, *rem = &rem_s;
+ bf_init(ctx->bf_ctx, rem);
+ ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
+ BF_RNDZ);
+ bf_delete(rem);
+ } else {
+ goto math_mode_div_pow;
+ }
+ break;
+ case OP_math_mod:
+ /* Euclidian remainder */
+ ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
+ BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
+ break;
+ case OP_mod:
+ ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
+ BF_RNDZ) & BF_ST_INVALID_OP;
+ break;
+ case OP_pow:
+ if (b->sign) {
+ if (!is_math_mode(ctx)) {
+ ret = BF_ST_INVALID_OP;
+ } else {
+ math_mode_div_pow:
+ JS_FreeValue(ctx, res);
+ ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
+ if (ret != 0) {
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (ret < 0) {
+ return -1;
+ } else {
+ *pres = res;
+ return 0;
+ }
+ }
+ /* if no BigInt power operator defined, return a
+ bigfloat */
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ goto fail;
+ }
+ r = JS_GetBigFloat(res);
+ if (op == OP_div) {
+ ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
+ } else {
+ ret = bf_pow(r, a, b, ctx->fp_env.prec,
+ ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
+ }
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+ }
+ } else {
+ ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
+ }
+ break;
+
+ /* logical operations */
+ case OP_shl:
+ case OP_sar:
+ {
+ slimb_t v2;
+#if LIMB_BITS == 32
+ bf_get_int32(&v2, b, 0);
+ if (v2 == INT32_MIN)
+ v2 = INT32_MIN + 1;
+#else
+ bf_get_int64(&v2, b, 0);
+ if (v2 == INT64_MIN)
+ v2 = INT64_MIN + 1;
+#endif
+ if (op == OP_sar)
+ v2 = -v2;
+ ret = bf_set(r, a);
+ ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);
+ if (v2 < 0) {
+ ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);
+ }
+ }
+ break;
+ case OP_and:
+ ret = bf_logic_and(r, a, b);
+ break;
+ case OP_or:
+ ret = bf_logic_or(r, a, b);
+ break;
+ case OP_xor:
+ ret = bf_logic_xor(r, a, b);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = JS_CompactBigInt(ctx, res);
+ return 0;
+ fail:
+ JS_FreeValue(ctx, res);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return -1;
+}
+
+/* b must be a positive integer */
+static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
+{
+ bfdec_t b1;
+ int32_t b2;
+ int ret;
+
+ bfdec_init(b->ctx, &b1);
+ ret = bfdec_set(&b1, b);
+ if (ret) {
+ bfdec_delete(&b1);
+ return ret;
+ }
+ ret = bfdec_rint(&b1, BF_RNDZ);
+ if (ret) {
+ bfdec_delete(&b1);
+ return BF_ST_INVALID_OP; /* must be an integer */
+ }
+ ret = bfdec_get_int32(&b2, &b1);
+ bfdec_delete(&b1);
+ if (ret)
+ return ret; /* overflow */
+ if (b2 < 0)
+ return BF_ST_INVALID_OP; /* must be positive */
+ return bfdec_pow_ui(r, a, b2);
+}
+
+static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2)
+{
+ bfdec_t *r, *a, *b;
+ int ret;
+ JSValue res;
+
+ res = JS_NewBigDecimal(ctx);
+ if (JS_IsException(res))
+ goto fail;
+ r = JS_GetBigDecimal(res);
+
+ a = JS_ToBigDecimal(ctx, op1);
+ if (!a)
+ goto fail;
+ b = JS_ToBigDecimal(ctx, op2);
+ if (!b)
+ goto fail;
+ switch(op) {
+ case OP_add:
+ ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_sub:
+ ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_mul:
+ ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_div:
+ ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_math_mod:
+ /* Euclidian remainder */
+ ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
+ break;
+ case OP_mod:
+ ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
+ break;
+ case OP_pow:
+ ret = js_bfdec_pow(r, a, b);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+ fail:
+ JS_FreeValue(ctx, res);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return -1;
+}
+
+static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2, res;
+ uint32_t tag1, tag2;
+ int ret;
+ double d1, d2;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ /* fast path for float operations */
+ if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ goto handle_float64;
+ }
+
+ /* try to call an overloaded operator */
+ if ((tag1 == JS_TAG_OBJECT &&
+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
+ if (ret != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (ret < 0) {
+ goto exception;
+ } else {
+ sp[-2] = res;
+ return 0;
+ }
+ }
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
+ int32_t v1, v2;
+ int64_t v;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ switch(op) {
+ case OP_sub:
+ v = (int64_t)v1 - (int64_t)v2;
+ break;
+ case OP_mul:
+ v = (int64_t)v1 * (int64_t)v2;
+ if (is_math_mode(ctx) &&
+ (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))
+ goto handle_bigint;
+ if (v == 0 && (v1 | v2) < 0) {
+ sp[-2] = __JS_NewFloat64(ctx, -0.0);
+ return 0;
+ }
+ break;
+ case OP_div:
+ if (is_math_mode(ctx))
+ goto handle_bigint;
+ sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
+ return 0;
+ case OP_math_mod:
+ if (unlikely(v2 == 0)) {
+ throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
+ goto exception;
+ }
+ v = (int64_t)v1 % (int64_t)v2;
+ if (v < 0) {
+ if (v2 < 0)
+ v -= v2;
+ else
+ v += v2;
+ }
+ break;
+ case OP_mod:
+ if (v1 < 0 || v2 <= 0) {
+ sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
+ return 0;
+ } else {
+ v = (int64_t)v1 % (int64_t)v2;
+ }
+ break;
+ case OP_pow:
+ if (!is_math_mode(ctx)) {
+ sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
+ return 0;
+ } else {
+ goto handle_bigint;
+ }
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = JS_NewInt64(ctx, v);
+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
+ if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
+ if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
+ handle_bigint:
+ if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ goto exception;
+ } else {
+ double dr;
+ /* float64 result */
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ handle_float64:
+ if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
+ goto handle_bigint;
+ switch(op) {
+ case OP_sub:
+ dr = d1 - d2;
+ break;
+ case OP_mul:
+ dr = d1 * d2;
+ break;
+ case OP_div:
+ dr = d1 / d2;
+ break;
+ case OP_mod:
+ dr = fmod(d1, d2);
+ break;
+ case OP_math_mod:
+ d2 = fabs(d2);
+ dr = fmod(d1, d2);
+ /* XXX: loss of accuracy if dr < 0 */
+ if (dr < 0)
+ dr += d2;
+ break;
+ case OP_pow:
+ dr = js_pow(d1, d2);
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = __JS_NewFloat64(ctx, dr);
+ }
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2, res;
+ uint32_t tag1, tag2;
+ int ret;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ /* fast path for float64 */
+ if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
+ double d1, d2;
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
+ return 0;
+ }
+
+ if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
+ /* try to call an overloaded operator */
+ if ((tag1 == JS_TAG_OBJECT &&
+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&
+ tag2 != JS_TAG_STRING)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&
+ tag1 != JS_TAG_STRING))) {
+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,
+ FALSE, HINT_NONE);
+ if (ret != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (ret < 0) {
+ goto exception;
+ } else {
+ sp[-2] = res;
+ return 0;
+ }
+ }
+ }
+
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ }
+
+ if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
+ sp[-2] = JS_ConcatString(ctx, op1, op2);
+ if (JS_IsException(sp[-2]))
+ goto exception;
+ return 0;
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
+ int32_t v1, v2;
+ int64_t v;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ v = (int64_t)v1 + (int64_t)v2;
+ sp[-2] = JS_NewInt64(ctx, v);
+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
+ if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
+ if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
+ handle_bigint:
+ if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
+ goto exception;
+ } else {
+ double d1, d2;
+ /* float64 result */
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
+ goto handle_bigint;
+ sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
+ }
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline WARN_UNUSED int js_binary_logic_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2, res;
+ int ret;
+ uint32_t tag1, tag2;
+ uint32_t v1, v2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ /* try to call an overloaded operator */
+ if ((tag1 == JS_TAG_OBJECT &&
+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
+ if (ret != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (ret < 0) {
+ goto exception;
+ } else {
+ sp[-2] = res;
+ return 0;
+ }
+ }
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+
+ if (is_math_mode(ctx))
+ goto bigint_op;
+
+ tag1 = JS_VALUE_GET_TAG(op1);
+ tag2 = JS_VALUE_GET_TAG(op2);
+ if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
+ if (tag1 != tag2) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ JS_ThrowTypeError(ctx, "both operands must be bigint");
+ goto exception;
+ } else {
+ bigint_op:
+ if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ goto exception;
+ }
+ } else {
+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
+ goto exception;
+ switch(op) {
+ case OP_shl:
+ r = v1 << (v2 & 0x1f);
+ break;
+ case OP_sar:
+ r = (int)v1 >> (v2 & 0x1f);
+ break;
+ case OP_and:
+ r = v1 & v2;
+ break;
+ case OP_or:
+ r = v1 | v2;
+ break;
+ case OP_xor:
+ r = v1 ^ v2;
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = JS_NewInt32(ctx, r);
+ }
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+/* Note: also used for bigint */
+static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,
+ JSValue op1, JSValue op2)
+{
+ bf_t a_s, b_s, *a, *b;
+ int res;
+
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ if (!a) {
+ JS_FreeValue(ctx, op2);
+ return -1;
+ }
+ b = JS_ToBigFloat(ctx, &b_s, op2);
+ if (!b) {
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ switch(op) {
+ case OP_lt:
+ res = bf_cmp_lt(a, b); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = bf_cmp_le(a, b); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = bf_cmp_lt(b, a); /* if NaN return false */
+ break;
+ case OP_gte:
+ res = bf_cmp_le(b, a); /* if NaN return false */
+ break;
+ case OP_eq:
+ res = bf_cmp_eq(a, b); /* if NaN return false */
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return res;
+}
+
+static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
+ JSValue op1, JSValue op2)
+{
+ bfdec_t *a, *b;
+ int res;
+
+ /* Note: binary floats are converted to bigdecimal with
+ toString(). It is not mathematically correct but is consistent
+ with the BigDecimal() constructor behavior */
+ op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ return -1;
+ }
+ op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ a = JS_ToBigDecimal(ctx, op1);
+ b = JS_ToBigDecimal(ctx, op2);
+
+ switch(op) {
+ case OP_lt:
+ res = bfdec_cmp_lt(a, b); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = bfdec_cmp_le(a, b); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = bfdec_cmp_lt(b, a); /* if NaN return false */
+ break;
+ case OP_gte:
+ res = bfdec_cmp_le(b, a); /* if NaN return false */
+ break;
+ case OP_eq:
+ res = bfdec_cmp_eq(a, b); /* if NaN return false */
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return res;
+}
+
+static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2, ret;
+ int res;
+ uint32_t tag1, tag2;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ /* try to call an overloaded operator */
+ if ((tag1 == JS_TAG_OBJECT &&
+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,
+ FALSE, HINT_NUMBER);
+ if (res != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (res < 0) {
+ goto exception;
+ } else {
+ sp[-2] = ret;
+ return 0;
+ }
+ }
+ }
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
+ JSString *p1, *p2;
+ p1 = JS_VALUE_GET_STRING(op1);
+ p2 = JS_VALUE_GET_STRING(op2);
+ res = js_string_compare(ctx, p1, p2);
+ switch(op) {
+ case OP_lt:
+ res = (res < 0);
+ break;
+ case OP_lte:
+ res = (res <= 0);
+ break;
+ case OP_gt:
+ res = (res > 0);
+ break;
+ default:
+ case OP_gte:
+ res = (res >= 0);
+ break;
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&
+ (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {
+ /* fast path for float64/int */
+ goto float64_compare;
+ } else {
+ if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
+ (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
+ !is_math_mode(ctx)) {
+ if (tag1 == JS_TAG_STRING) {
+ op1 = JS_StringToBigInt(ctx, op1);
+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
+ goto invalid_bigint_string;
+ }
+ if (tag2 == JS_TAG_STRING) {
+ op2 = JS_StringToBigInt(ctx, op2);
+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
+ invalid_bigint_string:
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ res = FALSE;
+ goto done;
+ }
+ }
+ } else {
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ }
+
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
+ res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
+ res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
+ res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else {
+ double d1, d2;
+
+ float64_compare:
+ /* can use floating point comparison */
+ if (tag1 == JS_TAG_FLOAT64) {
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ } else {
+ d1 = JS_VALUE_GET_INT(op1);
+ }
+ if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ } else {
+ d2 = JS_VALUE_GET_INT(op2);
+ }
+ switch(op) {
+ case OP_lt:
+ res = (d1 < d2); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = (d1 <= d2); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = (d1 > d2); /* if NaN return false */
+ break;
+ default:
+ case OP_gte:
+ res = (d1 >= d2); /* if NaN return false */
+ break;
+ }
+ }
+ }
+ done:
+ sp[-2] = JS_NewBool(ctx, res);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static BOOL tag_is_number(uint32_t tag)
+{
+ return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
+ tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT ||
+ tag == JS_TAG_BIG_DECIMAL);
+}
+
+static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+{
+ JSValue op1, op2, ret;
+ int res;
+ uint32_t tag1, tag2;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ redo:
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ if (tag_is_number(tag1) && tag_is_number(tag2)) {
+ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
+ res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
+ } else if ((tag1 == JS_TAG_FLOAT64 &&
+ (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
+ (tag2 == JS_TAG_FLOAT64 &&
+ (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
+ double d1, d2;
+ if (tag1 == JS_TAG_FLOAT64) {
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ } else {
+ d1 = JS_VALUE_GET_INT(op1);
+ }
+ if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ } else {
+ d2 = JS_VALUE_GET_INT(op2);
+ }
+ res = (d1 == d2);
+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
+ res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
+ res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else {
+ res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);
+ if (res < 0)
+ goto exception;
+ }
+ } else if (tag1 == tag2) {
+ if (tag1 == JS_TAG_OBJECT) {
+ /* try the fallback operator */
+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
+ is_neq ? OP_neq : OP_eq,
+ FALSE, HINT_NONE);
+ if (res != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (res < 0) {
+ goto exception;
+ } else {
+ sp[-2] = ret;
+ return 0;
+ }
+ }
+ }
+ res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
+ } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
+ (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
+ res = TRUE;
+ } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
+ (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
+
+ if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
+ !is_math_mode(ctx)) {
+ if (tag1 == JS_TAG_STRING) {
+ op1 = JS_StringToBigInt(ctx, op1);
+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
+ goto invalid_bigint_string;
+ }
+ if (tag2 == JS_TAG_STRING) {
+ op2 = JS_StringToBigInt(ctx, op2);
+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
+ invalid_bigint_string:
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ res = FALSE;
+ goto done;
+ }
+ }
+ } else {
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ }
+ res = js_strict_eq(ctx, op1, op2);
+ } else if (tag1 == JS_TAG_BOOL) {
+ op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
+ goto redo;
+ } else if (tag2 == JS_TAG_BOOL) {
+ op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
+ goto redo;
+ } else if ((tag1 == JS_TAG_OBJECT &&
+ (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {
+
+ /* try the fallback operator */
+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
+ is_neq ? OP_neq : OP_eq,
+ FALSE, HINT_NONE);
+ if (res != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (res < 0) {
+ goto exception;
+ } else {
+ sp[-2] = ret;
+ return 0;
+ }
+ }
+
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ goto redo;
+ } else {
+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
+ if ((JS_IsHTMLDDA(ctx, op1) &&
+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
+ (JS_IsHTMLDDA(ctx, op2) &&
+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
+ res = TRUE;
+ } else {
+ res = FALSE;
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ }
+ done:
+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ uint32_t v1, v2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ /* XXX: could forbid >>> in bignum mode */
+ if (!is_math_mode(ctx) &&
+ (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
+ JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
+ JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ /* cannot give an exception */
+ JS_ToUint32Free(ctx, &v1, op1);
+ JS_ToUint32Free(ctx, &v2, op2);
+ r = v1 >> (v2 & 0x1f);
+ sp[-2] = JS_NewUint32(ctx, r);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
+ int64_t exponent)
+{
+ bf_t r_s, *r = &r_s;
+ double d;
+ int ret;
+
+ /* always convert to Float64 */
+ bf_init(ctx->bf_ctx, r);
+ ret = bf_mul_pow_radix(r, a, 10, exponent,
+ 53, bf_set_exp_bits(11) | BF_RNDN |
+ BF_FLAG_SUBNORMAL);
+ bf_get_float64(r, &d, BF_RNDN);
+ bf_delete(r);
+ if (ret & BF_ST_MEM_ERROR)
+ return JS_ThrowOutOfMemory(ctx);
+ else
+ return __JS_NewFloat64(ctx, d);
+}
+
+static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)
+{
+ bf_t a_s, *a, *r;
+ JSValue op1, op2, res;
+ int64_t e;
+ int ret;
+
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res))
+ return -1;
+ r = JS_GetBigFloat(res);
+ op1 = sp[-2];
+ op2 = sp[-1];
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ if (!a)
+ return -1;
+ if (JS_IsBigInt(ctx, op2)) {
+ ret = JS_ToBigInt64(ctx, &e, op2);
+ } else {
+ ret = JS_ToInt64(ctx, &e, op2);
+ }
+ if (ret) {
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, res);
+ return -1;
+ }
+
+ bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = res;
+ return 0;
+}
+
+#else /* !CONFIG_BIGNUM */
+
+static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "bigint is not supported");
+}
+
+JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
+{
+ return JS_ThrowUnsupportedBigint(ctx);
+}
+
+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
+{
+ return JS_ThrowUnsupportedBigint(ctx);
+}
+
+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ JS_ThrowUnsupportedBigint(ctx);
+ *pres = 0;
+ return -1;
+}
+
+static no_inline warn_unused int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1;
+ double d;
+
+ op1 = sp[-1];
+ if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+ }
+ switch(op) {
+ case OP_inc:
+ d++;
+ break;
+ case OP_dec:
+ d--;
+ break;
+ case OP_plus:
+ break;
+ case OP_neg:
+ d = -d;
+ break;
+ default:
+ abort();
+ }
+ sp[-1] = JS_NewFloat64(ctx, d);
+ return 0;
+}
+
+/* specific case necessary for correct return value semantics */
+static warn_unused int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+{
+ JSValue op1;
+ double d, r;
+
+ op1 = sp[-1];
+ if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+ }
+ r = d + 2 * (op - OP_post_dec) - 1;
+ sp[0] = JS_NewFloat64(ctx, r);
+ sp[-1] = JS_NewFloat64(ctx, d);
+ return 0;
+}
+
+static no_inline warn_unused int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2;
+ double d1, d2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) {
+ goto exception;
+ }
+ switch(op) {
+ case OP_sub:
+ r = d1 - d2;
+ break;
+ case OP_mul:
+ r = d1 * d2;
+ break;
+ case OP_div:
+ r = d1 / d2;
+ break;
+ case OP_mod:
+ r = fmod(d1, d2);
+ break;
+ case OP_pow:
+ r = js_pow(d1, d2);
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = JS_NewFloat64(ctx, r);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline warn_unused int js_add_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ uint32_t tag1, tag2;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ tag1 = JS_VALUE_GET_TAG(op1);
+ tag2 = JS_VALUE_GET_TAG(op2);
+ if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) &&
+ (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) {
+ goto add_numbers;
+ } else {
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_TAG(op1);
+ tag2 = JS_VALUE_GET_TAG(op2);
+ if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
+ sp[-2] = JS_ConcatString(ctx, op1, op2);
+ if (JS_IsException(sp[-2]))
+ goto exception;
+ } else {
+ double d1, d2;
+ add_numbers:
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ sp[-2] = JS_NewFloat64(ctx, d1 + d2);
+ }
+ }
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline warn_unused int js_binary_logic_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2;
+ uint32_t v1, v2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
+ goto exception;
+ switch(op) {
+ case OP_shl:
+ r = v1 << (v2 & 0x1f);
+ break;
+ case OP_sar:
+ r = (int)v1 >> (v2 & 0x1f);
+ break;
+ case OP_and:
+ r = v1 & v2;
+ break;
+ case OP_or:
+ r = v1 | v2;
+ break;
+ case OP_xor:
+ r = v1 ^ v2;
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = JS_NewInt32(ctx, r);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
+{
+ int32_t v1;
+
+ if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) {
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+ }
+ sp[-1] = JS_NewInt32(ctx, ~v1);
+ return 0;
+}
+
+static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2;
+ int res;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING &&
+ JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
+ JSString *p1, *p2;
+ p1 = JS_VALUE_GET_STRING(op1);
+ p2 = JS_VALUE_GET_STRING(op2);
+ res = js_string_compare(ctx, p1, p2);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ switch(op) {
+ case OP_lt:
+ res = (res < 0);
+ break;
+ case OP_lte:
+ res = (res <= 0);
+ break;
+ case OP_gt:
+ res = (res > 0);
+ break;
+ default:
+ case OP_gte:
+ res = (res >= 0);
+ break;
+ }
+ } else {
+ double d1, d2;
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ switch(op) {
+ case OP_lt:
+ res = (d1 < d2); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = (d1 <= d2); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = (d1 > d2); /* if NaN return false */
+ break;
+ default:
+ case OP_gte:
+ res = (d1 >= d2); /* if NaN return false */
+ break;
+ }
+ }
+ sp[-2] = JS_NewBool(ctx, res);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline warn_unused int js_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+{
+ JSValue op1, op2;
+ int tag1, tag2;
+ BOOL res;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ redo:
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ if (tag1 == tag2 ||
+ (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||
+ (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) {
+ res = js_strict_eq(ctx, op1, op2);
+ } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
+ (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
+ res = TRUE;
+ } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT ||
+ tag2 == JS_TAG_FLOAT64)) ||
+ (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT ||
+ tag1 == JS_TAG_FLOAT64))) {
+ double d1;
+ double d2;
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ res = (d1 == d2);
+ } else if (tag1 == JS_TAG_BOOL) {
+ op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
+ goto redo;
+ } else if (tag2 == JS_TAG_BOOL) {
+ op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
+ goto redo;
+ } else if (tag1 == JS_TAG_OBJECT &&
+ (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) {
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ goto redo;
+ } else if (tag2 == JS_TAG_OBJECT &&
+ (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) {
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ goto redo;
+ } else {
+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
+ if ((JS_IsHTMLDDA(ctx, op1) &&
+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
+ (JS_IsHTMLDDA(ctx, op2) &&
+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
+ res = TRUE;
+ } else {
+ res = FALSE;
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ }
+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ uint32_t v1, v2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (unlikely(JS_ToUint32Free(ctx, &v2, op2)))
+ goto exception;
+ r = v1 >> (v2 & 0x1f);
+ sp[-2] = JS_NewUint32(ctx, r);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+#endif /* !CONFIG_BIGNUM */
+
+/* XXX: Should take JSValueConst arguments */
+static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
+ JSStrictEqModeEnum eq_mode)
+{
+ BOOL res;
+ int tag1, tag2;
+ double d1, d2;
+
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ switch(tag1) {
+ case JS_TAG_BOOL:
+ if (tag1 != tag2) {
+ res = FALSE;
+ } else {
+ res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
+ goto done_no_free;
+ }
+ break;
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ res = (tag1 == tag2);
+ break;
+ case JS_TAG_STRING:
+ {
+ JSString *p1, *p2;
+ if (tag1 != tag2) {
+ res = FALSE;
+ } else {
+ p1 = JS_VALUE_GET_STRING(op1);
+ p2 = JS_VALUE_GET_STRING(op2);
+ res = (js_string_compare(ctx, p1, p2) == 0);
+ }
+ }
+ break;
+ case JS_TAG_SYMBOL:
+ {
+ JSAtomStruct *p1, *p2;
+ if (tag1 != tag2) {
+ res = FALSE;
+ } else {
+ p1 = JS_VALUE_GET_PTR(op1);
+ p2 = JS_VALUE_GET_PTR(op2);
+ res = (p1 == p2);
+ }
+ }
+ break;
+ case JS_TAG_OBJECT:
+ if (tag1 != tag2)
+ res = FALSE;
+ else
+ res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
+ break;
+ case JS_TAG_INT:
+ d1 = JS_VALUE_GET_INT(op1);
+ if (tag2 == JS_TAG_INT) {
+ d2 = JS_VALUE_GET_INT(op2);
+ goto number_test;
+ } else if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ goto number_test;
+ } else {
+ res = FALSE;
+ }
+ break;
+ case JS_TAG_FLOAT64:
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ } else if (tag2 == JS_TAG_INT) {
+ d2 = JS_VALUE_GET_INT(op2);
+ } else {
+ res = FALSE;
+ break;
+ }
+ number_test:
+ if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
+ JSFloat64Union u1, u2;
+ /* NaN is not always normalized, so this test is necessary */
+ if (isnan(d1) || isnan(d2)) {
+ res = isnan(d1) == isnan(d2);
+ } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
+ res = (d1 == d2); /* +0 == -0 */
+ } else {
+ u1.d = d1;
+ u2.d = d2;
+ res = (u1.u64 == u2.u64); /* +0 != -0 */
+ }
+ } else {
+ res = (d1 == d2); /* if NaN return false and +0 == -0 */
+ }
+ goto done_no_free;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ {
+ bf_t a_s, *a, b_s, *b;
+ if (tag1 != tag2) {
+ res = FALSE;
+ break;
+ }
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ b = JS_ToBigFloat(ctx, &b_s, op2);
+ res = bf_cmp_eq(a, b);
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ }
+ break;
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p1, *p2;
+ const bf_t *a, *b;
+ if (tag1 != tag2) {
+ res = FALSE;
+ break;
+ }
+ p1 = JS_VALUE_GET_PTR(op1);
+ p2 = JS_VALUE_GET_PTR(op2);
+ a = &p1->num;
+ b = &p2->num;
+ if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
+ if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
+ a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
+ res = TRUE;
+ } else {
+ res = (bf_cmp_full(a, b) == 0);
+ }
+ } else {
+ res = bf_cmp_eq(a, b);
+ }
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *p1, *p2;
+ const bfdec_t *a, *b;
+ if (tag1 != tag2) {
+ res = FALSE;
+ break;
+ }
+ p1 = JS_VALUE_GET_PTR(op1);
+ p2 = JS_VALUE_GET_PTR(op2);
+ a = &p1->num;
+ b = &p2->num;
+ res = bfdec_cmp_eq(a, b);
+ }
+ break;
+#endif
+ default:
+ res = FALSE;
+ break;
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ done_no_free:
+ return res;
+}
+
+static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)
+{
+ return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
+}
+
+static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)
+{
+ return js_strict_eq2(ctx,
+ JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
+ JS_EQ_SAME_VALUE);
+}
+
+static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
+{
+ return js_strict_eq2(ctx,
+ JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
+ JS_EQ_SAME_VALUE_ZERO);
+}
+
+static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+{
+ BOOL res;
+ res = js_strict_eq(ctx, sp[-2], sp[-1]);
+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);
+ return 0;
+}
+
+static warn_unused int js_operator_in(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ JSAtom atom;
+ int ret;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+
+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {
+ JS_ThrowTypeError(ctx, "invalid 'in' operand");
+ return -1;
+ }
+ atom = JS_ValueToAtom(ctx, op1);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return -1;
+ ret = JS_HasProperty(ctx, op2, atom);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ return -1;
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = JS_NewBool(ctx, ret);
+ return 0;
+}
+
+static warn_unused int js_has_unscopable(JSContext *ctx, JSValueConst obj,
+ JSAtom atom)
+{
+ JSValue arr, val;
+ int ret;
+
+ arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);
+ if (JS_IsException(arr))
+ return -1;
+ ret = 0;
+ if (JS_IsObject(arr)) {
+ val = JS_GetProperty(ctx, arr, atom);
+ ret = JS_ToBoolFree(ctx, val);
+ }
+ JS_FreeValue(ctx, arr);
+ return ret;
+}
+
+static warn_unused int js_operator_instanceof(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ BOOL ret;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ ret = JS_IsInstanceOf(ctx, op1, op2);
+ if (ret < 0)
+ return ret;
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = JS_NewBool(ctx, ret);
+ return 0;
+}
+
+static warn_unused int js_operator_typeof(JSContext *ctx, JSValueConst op1)
+{
+ JSAtom atom;
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_NORM_TAG(op1);
+ switch(tag) {
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ atom = JS_ATOM_bigint;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ atom = JS_ATOM_bigfloat;
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ atom = JS_ATOM_bigdecimal;
+ break;
+#endif
+ case JS_TAG_INT:
+ case JS_TAG_FLOAT64:
+ atom = JS_ATOM_number;
+ break;
+ case JS_TAG_UNDEFINED:
+ atom = JS_ATOM_undefined;
+ break;
+ case JS_TAG_BOOL:
+ atom = JS_ATOM_boolean;
+ break;
+ case JS_TAG_STRING:
+ atom = JS_ATOM_string;
+ break;
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p;
+ p = JS_VALUE_GET_OBJ(op1);
+ if (unlikely(p->is_HTMLDDA))
+ atom = JS_ATOM_undefined;
+ else if (JS_IsFunction(ctx, op1))
+ atom = JS_ATOM_function;
+ else
+ goto obj_type;
+ }
+ break;
+ case JS_TAG_NULL:
+ obj_type:
+ atom = JS_ATOM_object;
+ break;
+ case JS_TAG_SYMBOL:
+ atom = JS_ATOM_symbol;
+ break;
+ default:
+ atom = JS_ATOM_unknown;
+ break;
+ }
+ return atom;
+}
+
+static warn_unused int js_operator_delete(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ JSAtom atom;
+ int ret;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ atom = JS_ValueToAtom(ctx, op2);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return -1;
+ ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);
+ JS_FreeAtom(ctx, atom);
+ if (unlikely(ret < 0))
+ return -1;
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = JS_NewBool(ctx, ret);
+ return 0;
+}
+
+static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ThrowTypeError(ctx, "invalid property access");
+}
+
+/* XXX: not 100% compatible, but mozilla seems to use a similar
+ implementation to ensure that caller in non strict mode does not
+ throw (ES5 compatibility) */
+static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
+ if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {
+ return js_throw_type_error(ctx, this_val, 0, NULL);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_function_proto_fileName(JSContext *ctx,
+ JSValueConst this_val)
+{
+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
+ if (b && b->has_debug) {
+ return JS_AtomToString(ctx, b->debug.filename);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_function_proto_lineNumber(JSContext *ctx,
+ JSValueConst this_val)
+{
+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
+ if (b && b->has_debug) {
+ return JS_NewInt32(ctx, b->debug.line_num);
+ }
+ return JS_UNDEFINED;
+}
+
+static int js_arguments_define_own_property(JSContext *ctx,
+ JSValueConst this_obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter, int flags)
+{
+ JSObject *p;
+ uint32_t idx;
+ p = JS_VALUE_GET_OBJ(this_obj);
+ /* convert to normal array when redefining an existing numeric field */
+ if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&
+ idx < p->u.array.count) {
+ if (convert_fast_array_to_array(ctx, p))
+ return -1;
+ }
+ /* run the default define own property */
+ return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
+ flags | JS_PROP_NO_EXOTIC);
+}
+
+static const JSClassExoticMethods js_arguments_exotic_methods = {
+ .define_own_property = js_arguments_define_own_property,
+};
+
+static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
+{
+ JSValue val, *tab;
+ JSProperty *pr;
+ JSObject *p;
+ int i;
+
+ val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_ARGUMENTS);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_OBJ(val);
+
+ /* add the length field (cannot fail) */
+ pr = add_property(ctx, p, JS_ATOM_length,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ pr->u.value = JS_NewInt32(ctx, argc);
+
+ /* initialize the fast array part */
+ tab = NULL;
+ if (argc > 0) {
+ tab = js_malloc(ctx, sizeof(tab[0]) * argc);
+ if (!tab) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ for(i = 0; i < argc; i++) {
+ tab[i] = JS_DupValue(ctx, argv[i]);
+ }
+ }
+ p->u.array.u.values = tab;
+ p->u.array.count = argc;
+
+ JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
+ JS_DupValue(ctx, ctx->array_proto_values),
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
+ /* add callee property to throw a TypeError in strict mode */
+ JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,
+ ctx->throw_type_error, ctx->throw_type_error,
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET);
+ return val;
+}
+
+#define GLOBAL_VAR_OFFSET 0x40000000
+#define ARGUMENT_VAR_OFFSET 0x20000000
+
+/* legacy arguments object: add references to the function arguments */
+static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
+ JSValueConst *argv,
+ JSStackFrame *sf, int arg_count)
+{
+ JSValue val;
+ JSProperty *pr;
+ JSObject *p;
+ int i;
+
+ val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_MAPPED_ARGUMENTS);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_OBJ(val);
+
+ /* add the length field (cannot fail) */
+ pr = add_property(ctx, p, JS_ATOM_length,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ pr->u.value = JS_NewInt32(ctx, argc);
+
+ for(i = 0; i < arg_count; i++) {
+ JSVarRef *var_ref;
+ var_ref = get_var_ref(ctx, sf, i, TRUE);
+ if (!var_ref)
+ goto fail;
+ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
+ if (!pr) {
+ free_var_ref(ctx->rt, var_ref);
+ goto fail;
+ }
+ pr->u.var_ref = var_ref;
+ }
+
+ /* the arguments not mapped to the arguments of the function can
+ be normal properties */
+ for(i = arg_count; i < argc; i++) {
+ if (JS_DefinePropertyValueUint32(ctx, val, i,
+ JS_DupValue(ctx, argv[i]),
+ JS_PROP_C_W_E) < 0)
+ goto fail;
+ }
+
+ JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
+ JS_DupValue(ctx, ctx->array_proto_values),
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
+ /* callee returns this function in non strict mode */
+ JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,
+ JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
+ return val;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int i, ret;
+
+ val = JS_NewArray(ctx);
+ if (JS_IsException(val))
+ return val;
+ for (i = first; i < argc; i++) {
+ ret = JS_DefinePropertyValueUint32(ctx, val, i - first,
+ JS_DupValue(ctx, argv[i]),
+ JS_PROP_C_W_E);
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ }
+ return val;
+}
+
+static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
+{
+ JSObject *p;
+ JSPropertyEnum *tab_atom;
+ int i;
+ JSValue enum_obj, obj1;
+ JSForInIterator *it;
+ uint32_t tag, tab_atom_count;
+
+ tag = JS_VALUE_GET_TAG(obj);
+ if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {
+ obj = JS_ToObjectFree(ctx, obj);
+ }
+
+ it = js_malloc(ctx, sizeof(*it));
+ if (!it) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
+ if (JS_IsException(enum_obj)) {
+ js_free(ctx, it);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ it->is_array = FALSE;
+ it->obj = obj;
+ it->idx = 0;
+ p = JS_VALUE_GET_OBJ(enum_obj);
+ p->u.for_in_iterator = it;
+
+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
+ return enum_obj;
+
+ /* fast path: assume no enumerable properties in the prototype chain */
+ obj1 = JS_DupValue(ctx, obj);
+ for(;;) {
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsNull(obj1))
+ break;
+ if (JS_IsException(obj1))
+ goto fail;
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
+ JS_VALUE_GET_OBJ(obj1),
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
+ JS_FreeValue(ctx, obj1);
+ goto fail;
+ }
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ if (tab_atom_count != 0) {
+ JS_FreeValue(ctx, obj1);
+ goto slow_path;
+ }
+ /* must check for timeout to avoid infinite loop */
+ if (js_poll_interrupts(ctx)) {
+ JS_FreeValue(ctx, obj1);
+ goto fail;
+ }
+ }
+
+ p = JS_VALUE_GET_OBJ(obj);
+
+ if (p->fast_array) {
+ JSShape *sh;
+ JSShapeProperty *prs;
+ /* check that there are no enumerable normal fields */
+ sh = p->shape;
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ if (prs->flags & JS_PROP_ENUMERABLE)
+ goto normal_case;
+ }
+ /* for fast arrays, we only store the number of elements */
+ it->is_array = TRUE;
+ it->array_length = p->u.array.count;
+ } else {
+ normal_case:
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))
+ goto fail;
+ for(i = 0; i < tab_atom_count; i++) {
+ JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);
+ }
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ }
+ return enum_obj;
+
+ slow_path:
+ /* non enumerable properties hide the enumerables ones in the
+ prototype chain */
+ obj1 = JS_DupValue(ctx, obj);
+ for(;;) {
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
+ JS_VALUE_GET_OBJ(obj1),
+ JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
+ JS_FreeValue(ctx, obj1);
+ goto fail;
+ }
+ for(i = 0; i < tab_atom_count; i++) {
+ JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,
+ (tab_atom[i].is_enumerable ?
+ JS_PROP_ENUMERABLE : 0));
+ }
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsNull(obj1))
+ break;
+ if (JS_IsException(obj1))
+ goto fail;
+ /* must check for timeout to avoid infinite loop */
+ if (js_poll_interrupts(ctx)) {
+ JS_FreeValue(ctx, obj1);
+ goto fail;
+ }
+ }
+ return enum_obj;
+
+ fail:
+ JS_FreeValue(ctx, enum_obj);
+ return JS_EXCEPTION;
+}
+
+/* obj -> enum_obj */
+static warn_unused int js_for_in_start(JSContext *ctx, JSValue *sp)
+{
+ sp[-1] = build_for_in_iterator(ctx, sp[-1]);
+ if (JS_IsException(sp[-1]))
+ return -1;
+ return 0;
+}
+
+/* enum_obj -> enum_obj value done */
+static warn_unused int js_for_in_next(JSContext *ctx, JSValue *sp)
+{
+ JSValueConst enum_obj;
+ JSObject *p;
+ JSAtom prop;
+ JSForInIterator *it;
+ int ret;
+
+ enum_obj = sp[-1];
+ /* fail safe */
+ if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
+ goto done;
+ p = JS_VALUE_GET_OBJ(enum_obj);
+ if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
+ goto done;
+ it = p->u.for_in_iterator;
+
+ for(;;) {
+ if (it->is_array) {
+ if (it->idx >= it->array_length)
+ goto done;
+ prop = JS_AtomFromUInt32(it->idx);
+ it->idx++;
+ } else {
+ JSShape *sh = p->shape;
+ JSShapeProperty *prs;
+ if (it->idx >= sh->prop_count)
+ goto done;
+ prs = get_shape_prop(sh) + it->idx;
+ prop = prs->atom;
+ it->idx++;
+ if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))
+ continue;
+ }
+ /* check if the property was deleted */
+ ret = JS_HasProperty(ctx, it->obj, prop);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ break;
+ }
+ /* return the property */
+ sp[0] = JS_AtomToValue(ctx, prop);
+ sp[1] = JS_FALSE;
+ return 0;
+ done:
+ /* return the end */
+ sp[0] = JS_UNDEFINED;
+ sp[1] = JS_TRUE;
+ return 0;
+}
+
+static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,
+ JSValueConst method)
+{
+ JSValue enum_obj;
+
+ enum_obj = JS_Call(ctx, method, obj, 0, NULL);
+ if (JS_IsException(enum_obj))
+ return enum_obj;
+ if (!JS_IsObject(enum_obj)) {
+ JS_FreeValue(ctx, enum_obj);
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ }
+ return enum_obj;
+}
+
+static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)
+{
+ JSValue method, ret, sync_iter;
+
+ if (is_async) {
+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);
+ if (JS_IsException(method))
+ return method;
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(method))
+ return method;
+ sync_iter = JS_GetIterator2(ctx, obj, method);
+ JS_FreeValue(ctx, method);
+ if (JS_IsException(sync_iter))
+ return sync_iter;
+ ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);
+ JS_FreeValue(ctx, sync_iter);
+ return ret;
+ }
+ } else {
+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(method))
+ return method;
+ }
+ if (!JS_IsFunction(ctx, method)) {
+ JS_FreeValue(ctx, method);
+ return JS_ThrowTypeError(ctx, "value is not iterable");
+ }
+ ret = JS_GetIterator2(ctx, obj, method);
+ JS_FreeValue(ctx, method);
+ return ret;
+}
+
+/* return *pdone = 2 if the iterator object is not parsed */
+static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,
+ JSValueConst method,
+ int argc, JSValueConst *argv, int *pdone)
+{
+ JSValue obj;
+
+ /* fast path for the built-in iterators (avoid creating the
+ intermediate result object) */
+ if (JS_IsObject(method)) {
+ JSObject *p = JS_VALUE_GET_OBJ(method);
+ if (p->class_id == JS_CLASS_C_FUNCTION &&
+ p->u.cfunc.cproto == JS_CFUNC_iterator_next) {
+ JSCFunctionType func;
+ JSValueConst args[1];
+
+ /* in case the function expects one argument */
+ if (argc == 0) {
+ args[0] = JS_UNDEFINED;
+ argv = args;
+ }
+ func = p->u.cfunc.c_function;
+ return func.iterator_next(ctx, enum_obj, argc, argv,
+ pdone, p->u.cfunc.magic);
+ }
+ }
+ obj = JS_Call(ctx, method, enum_obj, argc, argv);
+ if (JS_IsException(obj))
+ goto fail;
+ if (!JS_IsObject(obj)) {
+ JS_FreeValue(ctx, obj);
+ JS_ThrowTypeError(ctx, "iterator must return an object");
+ goto fail;
+ }
+ *pdone = 2;
+ return obj;
+ fail:
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,
+ JSValueConst method,
+ int argc, JSValueConst *argv, BOOL *pdone)
+{
+ JSValue obj, value, done_val;
+ int done;
+
+ obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);
+ if (JS_IsException(obj))
+ goto fail;
+ if (done != 2) {
+ *pdone = done;
+ return obj;
+ } else {
+ done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
+ if (JS_IsException(done_val))
+ goto fail;
+ *pdone = JS_ToBoolFree(ctx, done_val);
+ value = JS_UNDEFINED;
+ if (!*pdone) {
+ value = JS_GetProperty(ctx, obj, JS_ATOM_value);
+ }
+ JS_FreeValue(ctx, obj);
+ return value;
+ }
+ fail:
+ JS_FreeValue(ctx, obj);
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+}
+
+/* return < 0 in case of exception */
+static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
+ BOOL is_exception_pending)
+{
+ JSValue method, ret, ex_obj;
+ int res;
+
+ if (is_exception_pending) {
+ ex_obj = ctx->rt->current_exception;
+ ctx->rt->current_exception = JS_NULL;
+ res = -1;
+ } else {
+ ex_obj = JS_UNDEFINED;
+ res = 0;
+ }
+ method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);
+ if (JS_IsException(method)) {
+ res = -1;
+ goto done;
+ }
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
+ goto done;
+ }
+ ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);
+ if (!is_exception_pending) {
+ if (JS_IsException(ret)) {
+ res = -1;
+ } else if (!JS_IsObject(ret)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ res = -1;
+ }
+ }
+ JS_FreeValue(ctx, ret);
+ done:
+ if (is_exception_pending) {
+ JS_Throw(ctx, ex_obj);
+ }
+ return res;
+}
+
+/* obj -> enum_rec (3 slots) */
+static warn_unused int js_for_of_start(JSContext *ctx, JSValue *sp,
+ BOOL is_async)
+{
+ JSValue op1, obj, method;
+ op1 = sp[-1];
+ obj = JS_GetIterator(ctx, op1, is_async);
+ if (JS_IsException(obj))
+ return -1;
+ JS_FreeValue(ctx, op1);
+ sp[-1] = obj;
+ method = JS_GetProperty(ctx, obj, JS_ATOM_next);
+ if (JS_IsException(method))
+ return -1;
+ sp[0] = method;
+ return 0;
+}
+
+/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
+ objs. If 'done' is true or in case of exception, 'enum_rec' is set
+ to undefined. If 'done' is true, 'value' is always set to
+ undefined. */
+static warn_unused int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
+{
+ JSValue value = JS_UNDEFINED;
+ int done = 1;
+
+ if (likely(!JS_IsUndefined(sp[offset]))) {
+ value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);
+ if (JS_IsException(value))
+ done = -1;
+ if (done) {
+ /* value is JS_UNDEFINED or JS_EXCEPTION */
+ /* replace the iteration object with undefined */
+ JS_FreeValue(ctx, sp[offset]);
+ sp[offset] = JS_UNDEFINED;
+ if (done < 0) {
+ return -1;
+ } else {
+ JS_FreeValue(ctx, value);
+ value = JS_UNDEFINED;
+ }
+ }
+ }
+ sp[0] = value;
+ sp[1] = JS_NewBool(ctx, done);
+ return 0;
+}
+
+static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
+ BOOL *pdone)
+{
+ JSValue done_val, value;
+ BOOL done;
+ done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
+ if (JS_IsException(done_val))
+ goto fail;
+ done = JS_ToBoolFree(ctx, done_val);
+ value = JS_GetProperty(ctx, obj, JS_ATOM_value);
+ if (JS_IsException(value))
+ goto fail;
+ *pdone = done;
+ return value;
+ fail:
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+}
+
+static warn_unused int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
+{
+ JSValue obj, value;
+ BOOL done;
+ obj = sp[-1];
+ if (!JS_IsObject(obj)) {
+ JS_ThrowTypeError(ctx, "iterator must return an object");
+ return -1;
+ }
+ value = JS_IteratorGetCompleteValue(ctx, obj, &done);
+ if (JS_IsException(value))
+ return -1;
+ JS_FreeValue(ctx, obj);
+ sp[-1] = value;
+ sp[0] = JS_NewBool(ctx, done);
+ return 0;
+}
+
+static JSValue js_create_iterator_result(JSContext *ctx,
+ JSValue val,
+ BOOL done)
+{
+ JSValue obj;
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj)) {
+ JS_FreeValue(ctx, val);
+ return obj;
+ }
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,
+ val, JS_PROP_C_W_E) < 0) {
+ goto fail;
+ }
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,
+ JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic);
+
+static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic);
+
+static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)
+{
+ /* Try and handle fast arrays explicitly */
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* Access an Array's internal JSValue array if available */
+static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
+ JSValue **arrpp, uint32_t *countp)
+{
+ /* Try and handle fast arrays explicitly */
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
+ *countp = p->u.array.count;
+ *arrpp = p->u.array.u.values;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static warn_unused int js_append_enumerate(JSContext *ctx, JSValue *sp)
+{
+ JSValue iterator, enumobj, method, value;
+ int is_array_iterator;
+ JSValue *arrp;
+ uint32_t i, count32, pos;
+
+ if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
+ JS_ThrowInternalError(ctx, "invalid index for append");
+ return -1;
+ }
+
+ pos = JS_VALUE_GET_INT(sp[-2]);
+
+ /* XXX: further optimisations:
+ - use ctx->array_proto_values?
+ - check if array_iterator_prototype next method is built-in and
+ avoid constructing actual iterator object?
+ - build this into js_for_of_start and use in all `for (x of o)` loops
+ */
+ iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);
+ if (JS_IsException(iterator))
+ return -1;
+ is_array_iterator = JS_IsCFunction(ctx, iterator,
+ (JSCFunction *)js_create_array_iterator,
+ JS_ITERATOR_KIND_VALUE);
+ JS_FreeValue(ctx, iterator);
+
+ enumobj = JS_GetIterator(ctx, sp[-1], FALSE);
+ if (JS_IsException(enumobj))
+ return -1;
+ method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);
+ if (JS_IsException(method)) {
+ JS_FreeValue(ctx, enumobj);
+ return -1;
+ }
+ if (is_array_iterator
+ && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)
+ && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
+ uint32_t len;
+ if (js_get_length32(ctx, &len, sp[-1]))
+ goto exception;
+ /* if len > count32, the elements >= count32 might be read in
+ the prototypes and might have side effects */
+ if (len != count32)
+ goto general_case;
+ /* Handle fast arrays explicitly */
+ for (i = 0; i < count32; i++) {
+ if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
+ JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
+ goto exception;
+ }
+ } else {
+ general_case:
+ for (;;) {
+ BOOL done;
+ value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
+ if (JS_IsException(value))
+ goto exception;
+ if (done) {
+ /* value is JS_UNDEFINED */
+ break;
+ }
+ if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)
+ goto exception;
+ }
+ }
+ /* Note: could raise an error if too many elements */
+ sp[-2] = JS_NewInt32(ctx, pos);
+ JS_FreeValue(ctx, enumobj);
+ JS_FreeValue(ctx, method);
+ return 0;
+
+exception:
+ JS_IteratorClose(ctx, enumobj, TRUE);
+ JS_FreeValue(ctx, enumobj);
+ JS_FreeValue(ctx, method);
+ return -1;
+}
+
+static warn_unused int JS_CopyDataProperties(JSContext *ctx,
+ JSValueConst target,
+ JSValueConst source,
+ JSValueConst excluded,
+ BOOL setprop)
+{
+ JSPropertyEnum *tab_atom;
+ JSValue val;
+ uint32_t i, tab_atom_count;
+ JSObject *p;
+ JSObject *pexcl = NULL;
+ int ret, gpn_flags;
+ JSPropertyDescriptor desc;
+ BOOL is_enumerable;
+
+ if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)
+ return 0;
+
+ if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)
+ pexcl = JS_VALUE_GET_OBJ(excluded);
+
+ p = JS_VALUE_GET_OBJ(source);
+
+ gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY;
+ if (p->is_exotic) {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it
+ introduces a visible change */
+ if (em && em->get_own_property_names) {
+ gpn_flags &= ~JS_GPN_ENUM_ONLY;
+ }
+ }
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
+ gpn_flags))
+ return -1;
+
+ for (i = 0; i < tab_atom_count; i++) {
+ if (pexcl) {
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);
+ if (ret) {
+ if (ret < 0)
+ goto exception;
+ continue;
+ }
+ }
+ if (!(gpn_flags & JS_GPN_ENUM_ONLY)) {
+ /* test if the property is enumerable */
+ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom);
+ if (ret < 0)
+ goto exception;
+ if (!ret)
+ continue;
+ is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0;
+ js_free_desc(ctx, &desc);
+ if (!is_enumerable)
+ continue;
+ }
+ val = JS_GetProperty(ctx, source, tab_atom[i].atom);
+ if (JS_IsException(val))
+ goto exception;
+ if (setprop)
+ ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);
+ else
+ ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val,
+ JS_PROP_C_W_E);
+ if (ret < 0)
+ goto exception;
+ }
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ return 0;
+ exception:
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ return -1;
+}
+
+/* only valid inside C functions */
+static JSValueConst JS_GetActiveFunction(JSContext *ctx)
+{
+ return ctx->rt->current_stack_frame->cur_func;
+}
+
+static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
+ int var_idx, BOOL is_arg)
+{
+ JSVarRef *var_ref;
+ struct list_head *el;
+
+ list_for_each(el, &sf->var_ref_list) {
+ var_ref = list_entry(el, JSVarRef, header.link);
+ if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
+ var_ref->header.ref_count++;
+ return var_ref;
+ }
+ }
+ /* create a new one */
+ var_ref = js_malloc(ctx, sizeof(JSVarRef));
+ if (!var_ref)
+ return NULL;
+ var_ref->header.ref_count = 1;
+ var_ref->is_detached = FALSE;
+ var_ref->is_arg = is_arg;
+ var_ref->var_idx = var_idx;
+ list_add_tail(&var_ref->header.link, &sf->var_ref_list);
+ if (is_arg)
+ var_ref->pvalue = &sf->arg_buf[var_idx];
+ else
+ var_ref->pvalue = &sf->var_buf[var_idx];
+ var_ref->value = JS_UNDEFINED;
+ return var_ref;
+}
+
+static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
+ JSFunctionBytecode *b,
+ JSVarRef **cur_var_refs,
+ JSStackFrame *sf)
+{
+ JSObject *p;
+ JSVarRef **var_refs;
+ int i;
+
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->u.func.function_bytecode = b;
+ p->u.func.home_object = NULL;
+ p->u.func.var_refs = NULL;
+ if (b->closure_var_count) {
+ var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
+ if (!var_refs)
+ goto fail;
+ p->u.func.var_refs = var_refs;
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ JSVarRef *var_ref;
+ if (cv->is_local) {
+ /* reuse the existing variable reference if it already exists */
+ var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);
+ if (!var_ref)
+ goto fail;
+ } else {
+ var_ref = cur_var_refs[cv->var_idx];
+ var_ref->header.ref_count++;
+ }
+ var_refs[i] = var_ref;
+ }
+ }
+ return func_obj;
+ fail:
+ /* bfunc is freed when func_obj is freed */
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)
+{
+ JSValue obj, this_val;
+ int ret;
+
+ this_val = JS_MKPTR(JS_TAG_OBJECT, p);
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ set_cycle_flag(ctx, obj);
+ set_cycle_flag(ctx, this_val);
+ ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,
+ JS_DupValue(ctx, this_val),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ if (ret < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+static const uint16_t func_kind_to_class_id[] = {
+ [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,
+ [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,
+ [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,
+ [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,
+};
+
+static JSValue js_closure(JSContext *ctx, JSValue bfunc,
+ JSVarRef **cur_var_refs,
+ JSStackFrame *sf)
+{
+ JSFunctionBytecode *b;
+ JSValue func_obj;
+ JSAtom name_atom;
+
+ b = JS_VALUE_GET_PTR(bfunc);
+ func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);
+ if (JS_IsException(func_obj)) {
+ JS_FreeValue(ctx, bfunc);
+ return JS_EXCEPTION;
+ }
+ func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);
+ if (JS_IsException(func_obj)) {
+ /* bfunc has been freed */
+ goto fail;
+ }
+ name_atom = b->func_name;
+ if (name_atom == JS_ATOM_NULL)
+ name_atom = JS_ATOM_empty_string;
+ js_function_set_properties(ctx, func_obj, name_atom,
+ b->defined_arg_count);
+
+ if (b->func_kind & JS_FUNC_GENERATOR) {
+ JSValue proto;
+ int proto_class_id;
+ /* generators have a prototype field which is used as
+ prototype for the generator object */
+ if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)
+ proto_class_id = JS_CLASS_ASYNC_GENERATOR;
+ else
+ proto_class_id = JS_CLASS_GENERATOR;
+ proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);
+ if (JS_IsException(proto))
+ goto fail;
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,
+ JS_PROP_WRITABLE);
+ } else if (b->has_prototype) {
+ /* add the 'prototype' property: delay instantiation to avoid
+ creating cycles for every javascript function. The prototype
+ object is created on the fly when first accessed */
+ JS_SetConstructorBit(ctx, func_obj, TRUE);
+ JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,
+ JS_AUTOINIT_ID_PROTOTYPE, NULL,
+ JS_PROP_WRITABLE);
+ }
+ return func_obj;
+ fail:
+ /* bfunc is freed when func_obj is freed */
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+}
+
+#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0)
+
+static int js_op_define_class(JSContext *ctx, JSValue *sp,
+ JSAtom class_name, int class_flags,
+ JSVarRef **cur_var_refs,
+ JSStackFrame *sf, BOOL is_computed_name)
+{
+ JSValue bfunc, parent_class, proto = JS_UNDEFINED;
+ JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;
+ JSFunctionBytecode *b;
+
+ parent_class = sp[-2];
+ bfunc = sp[-1];
+
+ if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {
+ if (JS_IsNull(parent_class)) {
+ parent_proto = JS_NULL;
+ parent_class = JS_DupValue(ctx, ctx->function_proto);
+ } else {
+ if (!JS_IsConstructor(ctx, parent_class)) {
+ JS_ThrowTypeError(ctx, "parent class must be constructor");
+ goto fail;
+ }
+ parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);
+ if (JS_IsException(parent_proto))
+ goto fail;
+ if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {
+ JS_ThrowTypeError(ctx, "parent prototype must be an object or null");
+ goto fail;
+ }
+ }
+ } else {
+ /* parent_class is JS_UNDEFINED in this case */
+ parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
+ parent_class = JS_DupValue(ctx, ctx->function_proto);
+ }
+ proto = JS_NewObjectProto(ctx, parent_proto);
+ if (JS_IsException(proto))
+ goto fail;
+
+ b = JS_VALUE_GET_PTR(bfunc);
+ assert(b->func_kind == JS_FUNC_NORMAL);
+ ctor = JS_NewObjectProtoClass(ctx, parent_class,
+ JS_CLASS_BYTECODE_FUNCTION);
+ if (JS_IsException(ctor))
+ goto fail;
+ ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);
+ bfunc = JS_UNDEFINED;
+ if (JS_IsException(ctor))
+ goto fail;
+ js_method_set_home_object(ctx, ctor, proto);
+ JS_SetConstructorBit(ctx, ctor, TRUE);
+
+ JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,
+ JS_NewInt32(ctx, b->defined_arg_count),
+ JS_PROP_CONFIGURABLE);
+
+ if (is_computed_name) {
+ if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],
+ JS_PROP_CONFIGURABLE) < 0)
+ goto fail;
+ } else {
+ if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)
+ goto fail;
+ }
+
+ /* the constructor property must be first. It can be overriden by
+ computed property names */
+ if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
+ JS_DupValue(ctx, ctor),
+ JS_PROP_CONFIGURABLE |
+ JS_PROP_WRITABLE | JS_PROP_THROW) < 0)
+ goto fail;
+ /* set the prototype property */
+ if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,
+ JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)
+ goto fail;
+ set_cycle_flag(ctx, ctor);
+ set_cycle_flag(ctx, proto);
+
+ JS_FreeValue(ctx, parent_proto);
+ JS_FreeValue(ctx, parent_class);
+
+ sp[-2] = ctor;
+ sp[-1] = proto;
+ return 0;
+ fail:
+ JS_FreeValue(ctx, parent_class);
+ JS_FreeValue(ctx, parent_proto);
+ JS_FreeValue(ctx, bfunc);
+ JS_FreeValue(ctx, proto);
+ JS_FreeValue(ctx, ctor);
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
+{
+ struct list_head *el, *el1;
+ JSVarRef *var_ref;
+ int var_idx;
+
+ list_for_each_safe(el, el1, &sf->var_ref_list) {
+ var_ref = list_entry(el, JSVarRef, header.link);
+ var_idx = var_ref->var_idx;
+ if (var_ref->is_arg)
+ var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
+ else
+ var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]);
+ var_ref->pvalue = &var_ref->value;
+ /* the reference is no longer to a local variable */
+ var_ref->is_detached = TRUE;
+ add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
+ }
+}
+
+static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)
+{
+ struct list_head *el, *el1;
+ JSVarRef *var_ref;
+ int var_idx = idx;
+
+ list_for_each_safe(el, el1, &sf->var_ref_list) {
+ var_ref = list_entry(el, JSVarRef, header.link);
+ if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {
+ var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
+ var_ref->pvalue = &var_ref->value;
+ list_del(&var_ref->header.link);
+ /* the reference is no longer to a local variable */
+ var_ref->is_detached = TRUE;
+ add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
+ }
+ }
+}
+
+#define JS_CALL_FLAG_COPY_ARGV (1 << 1)
+#define JS_CALL_FLAG_GENERATOR (1 << 2)
+
+static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSRuntime *rt = ctx->rt;
+ JSCFunctionType func;
+ JSObject *p;
+ JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
+ JSValue ret_val;
+ JSValueConst *arg_buf;
+ int arg_count, i;
+ JSCFunctionEnum cproto;
+
+ p = JS_VALUE_GET_OBJ(func_obj);
+ cproto = p->u.cfunc.cproto;
+ arg_count = p->u.cfunc.length;
+
+ /* better to always check stack overflow */
+ if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))
+ return JS_ThrowStackOverflow(ctx);
+
+ prev_sf = rt->current_stack_frame;
+ sf->prev_frame = prev_sf;
+ rt->current_stack_frame = sf;
+ ctx = p->u.cfunc.realm; /* change the current realm */
+
+#ifdef CONFIG_BIGNUM
+ /* we only propagate the bignum mode as some runtime functions
+ test it */
+ if (prev_sf)
+ sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
+ else
+ sf->js_mode = 0;
+#else
+ sf->js_mode = 0;
+#endif
+ sf->cur_func = func_obj;
+ sf->arg_count = argc;
+ arg_buf = argv;
+
+ if (unlikely(argc < arg_count)) {
+ /* ensure that at least argc_count arguments are readable */
+ arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);
+ for(i = 0; i < argc; i++)
+ arg_buf[i] = argv[i];
+ for(i = argc; i < arg_count; i++)
+ arg_buf[i] = JS_UNDEFINED;
+ sf->arg_count = arg_count;
+ }
+ sf->arg_buf = arg_buf;
+
+ func = p->u.cfunc.c_function;
+ switch(cproto) {
+ case JS_CFUNC_constructor:
+ case JS_CFUNC_constructor_or_func:
+ if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
+ if (cproto == JS_CFUNC_constructor) {
+ not_a_constructor:
+ ret_val = JS_ThrowTypeError(ctx, "must be called with new");
+ break;
+ } else {
+ this_obj = JS_UNDEFINED;
+ }
+ }
+ /* here this_obj is new_target */
+ /* fall thru */
+ case JS_CFUNC_generic:
+ ret_val = func.generic(ctx, this_obj, argc, arg_buf);
+ break;
+ case JS_CFUNC_constructor_magic:
+ case JS_CFUNC_constructor_or_func_magic:
+ if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
+ if (cproto == JS_CFUNC_constructor_magic) {
+ goto not_a_constructor;
+ } else {
+ this_obj = JS_UNDEFINED;
+ }
+ }
+ /* fall thru */
+ case JS_CFUNC_generic_magic:
+ ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,
+ p->u.cfunc.magic);
+ break;
+ case JS_CFUNC_getter:
+ ret_val = func.getter(ctx, this_obj);
+ break;
+ case JS_CFUNC_setter:
+ ret_val = func.setter(ctx, this_obj, arg_buf[0]);
+ break;
+ case JS_CFUNC_getter_magic:
+ ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);
+ break;
+ case JS_CFUNC_setter_magic:
+ ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);
+ break;
+ case JS_CFUNC_f_f:
+ {
+ double d1;
+
+ if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
+ ret_val = JS_EXCEPTION;
+ break;
+ }
+ ret_val = JS_NewFloat64(ctx, func.f_f(d1));
+ }
+ break;
+ case JS_CFUNC_f_f_f:
+ {
+ double d1, d2;
+
+ if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
+ ret_val = JS_EXCEPTION;
+ break;
+ }
+ if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {
+ ret_val = JS_EXCEPTION;
+ break;
+ }
+ ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));
+ }
+ break;
+ case JS_CFUNC_iterator_next:
+ {
+ int done;
+ ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,
+ &done, p->u.cfunc.magic);
+ if (!JS_IsException(ret_val) && done != 2) {
+ ret_val = js_create_iterator_result(ctx, ret_val, done);
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+
+ rt->current_stack_frame = sf->prev_frame;
+ return ret_val;
+}
+
+static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSObject *p;
+ JSBoundFunction *bf;
+ JSValueConst *arg_buf, new_target;
+ int arg_count, i;
+
+ p = JS_VALUE_GET_OBJ(func_obj);
+ bf = p->u.bound_function;
+ arg_count = bf->argc + argc;
+ if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))
+ return JS_ThrowStackOverflow(ctx);
+ arg_buf = alloca(sizeof(JSValue) * arg_count);
+ for(i = 0; i < bf->argc; i++) {
+ arg_buf[i] = bf->argv[i];
+ }
+ for(i = 0; i < argc; i++) {
+ arg_buf[bf->argc + i] = argv[i];
+ }
+ if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
+ new_target = this_obj;
+ if (js_same_value(ctx, func_obj, new_target))
+ new_target = bf->func_obj;
+ return JS_CallConstructor2(ctx, bf->func_obj, new_target,
+ arg_count, arg_buf);
+ } else {
+ return JS_Call(ctx, bf->func_obj, bf->this_val,
+ arg_count, arg_buf);
+ }
+}
+
+/* argument of OP_special_object */
+typedef enum {
+ OP_SPECIAL_OBJECT_ARGUMENTS,
+ OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,
+ OP_SPECIAL_OBJECT_THIS_FUNC,
+ OP_SPECIAL_OBJECT_NEW_TARGET,
+ OP_SPECIAL_OBJECT_HOME_OBJECT,
+ OP_SPECIAL_OBJECT_VAR_OBJECT,
+ OP_SPECIAL_OBJECT_IMPORT_META,
+} OPSpecialObjectEnum;
+
+#define FUNC_RET_AWAIT 0
+#define FUNC_RET_YIELD 1
+#define FUNC_RET_YIELD_STAR 2
+
+/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
+static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ JSValueConst this_obj, JSValueConst new_target,
+ int argc, JSValue *argv, int flags)
+{
+ JSRuntime *rt = caller_ctx->rt;
+ JSContext *ctx;
+ JSObject *p;
+ JSFunctionBytecode *b;
+ JSStackFrame sf_s, *sf = &sf_s;
+ const uint8_t *pc;
+ int opcode, arg_allocated_size, i;
+ JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
+ JSVarRef **var_refs;
+ size_t alloca_size;
+
+#if !DIRECT_DISPATCH
+#define SWITCH(pc) switch (opcode = *pc++)
+#define CASE(op) case op
+#define DEFAULT default
+#define BREAK break
+#else
+ static const void * const dispatch_table[256] = {
+#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
+#if SHORT_OPCODES
+#define def(id, size, n_pop, n_push, f)
+#else
+#define def(id, size, n_pop, n_push, f) && case_default,
+#endif
+#include "quickjs-opcode.h"
+ [ OP_COUNT ... 255 ] = &&case_default
+ };
+#define SWITCH(pc) goto *dispatch_table[opcode = *(pc)++];
+#define CASE(op) case_ ## op
+#define DEFAULT case_default
+#define BREAK SWITCH(pc)
+#endif
+
+ if (js_poll_interrupts(caller_ctx))
+ return JS_EXCEPTION;
+ if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
+ if (flags & JS_CALL_FLAG_GENERATOR) {
+ JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
+ /* func_obj get contains a pointer to JSFuncAsyncState */
+ /* the stack frame is already allocated */
+ sf = &s->frame;
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ b = p->u.func.function_bytecode;
+ ctx = b->realm;
+ var_refs = p->u.func.var_refs;
+ local_buf = arg_buf = sf->arg_buf;
+ var_buf = sf->var_buf;
+ stack_buf = sf->var_buf + b->var_count;
+ sp = sf->cur_sp;
+ sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */
+ pc = sf->cur_pc;
+ sf->prev_frame = rt->current_stack_frame;
+ rt->current_stack_frame = sf;
+ if (s->throw_flag)
+ goto exception;
+ else
+ goto restart;
+ } else {
+ goto not_a_function;
+ }
+ }
+ p = JS_VALUE_GET_OBJ(func_obj);
+ if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
+ JSClassCall *call_func;
+ call_func = rt->class_array[p->class_id].call;
+ if (!call_func) {
+ not_a_function:
+ return JS_ThrowTypeError(caller_ctx, "not a function");
+ }
+ return call_func(caller_ctx, func_obj, this_obj, argc,
+ argv, flags);
+ }
+ b = p->u.func.function_bytecode;
+
+ if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {
+ arg_allocated_size = b->arg_count;
+ } else {
+ arg_allocated_size = 0;
+ }
+
+ alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
+ b->stack_size);
+ if (js_check_stack_overflow(rt, alloca_size))
+ return JS_ThrowStackOverflow(caller_ctx);
+
+ sf->js_mode = b->js_mode;
+ arg_buf = argv;
+ sf->arg_count = argc;
+ sf->cur_func = func_obj;
+ init_list_head(&sf->var_ref_list);
+ var_refs = p->u.func.var_refs;
+
+ local_buf = alloca(alloca_size);
+ if (unlikely(arg_allocated_size)) {
+ int n = min_int(argc, b->arg_count);
+ arg_buf = local_buf;
+ for(i = 0; i < n; i++)
+ arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);
+ for(; i < b->arg_count; i++)
+ arg_buf[i] = JS_UNDEFINED;
+ sf->arg_count = b->arg_count;
+ }
+ var_buf = local_buf + arg_allocated_size;
+ sf->var_buf = var_buf;
+ sf->arg_buf = arg_buf;
+
+ for(i = 0; i < b->var_count; i++)
+ var_buf[i] = JS_UNDEFINED;
+
+ stack_buf = var_buf + b->var_count;
+ sp = stack_buf;
+ pc = b->byte_code_buf;
+ sf->prev_frame = rt->current_stack_frame;
+ rt->current_stack_frame = sf;
+ ctx = b->realm; /* set the current realm */
+
+ if (ctx->handleFunctionEntered)
+ ctx->handleFunctionEntered(ctx, this_obj);
+
+ restart:
+ for(;;) {
+ int call_argc;
+ JSValue *call_argv;
+
+ SWITCH(pc) {
+ CASE(OP_push_i32):
+ *sp++ = JS_NewInt32(ctx, get_u32(pc));
+ pc += 4;
+ BREAK;
+ CASE(OP_push_const):
+ *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
+ pc += 4;
+ BREAK;
+#if SHORT_OPCODES
+ CASE(OP_push_minus1):
+ CASE(OP_push_0):
+ CASE(OP_push_1):
+ CASE(OP_push_2):
+ CASE(OP_push_3):
+ CASE(OP_push_4):
+ CASE(OP_push_5):
+ CASE(OP_push_6):
+ CASE(OP_push_7):
+ *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);
+ BREAK;
+ CASE(OP_push_i8):
+ *sp++ = JS_NewInt32(ctx, get_i8(pc));
+ pc += 1;
+ BREAK;
+ CASE(OP_push_i16):
+ *sp++ = JS_NewInt32(ctx, get_i16(pc));
+ pc += 2;
+ BREAK;
+ CASE(OP_push_const8):
+ *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
+ BREAK;
+ CASE(OP_fclosure8):
+ *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ BREAK;
+ CASE(OP_push_empty_string):
+ *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ BREAK;
+ CASE(OP_get_length):
+ {
+ JSValue val;
+
+ val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = val;
+ }
+ BREAK;
+#endif
+ CASE(OP_push_atom_value):
+ *sp++ = JS_AtomToValue(ctx, get_u32(pc));
+ pc += 4;
+ BREAK;
+ CASE(OP_undefined):
+ *sp++ = JS_UNDEFINED;
+ BREAK;
+ CASE(OP_null):
+ *sp++ = JS_NULL;
+ BREAK;
+ CASE(OP_push_this):
+ /* OP_push_this is only called at the start of a function */
+ {
+ JSValue val;
+ if (!(b->js_mode & JS_MODE_STRICT)) {
+ uint32_t tag = JS_VALUE_GET_TAG(this_obj);
+ if (likely(tag == JS_TAG_OBJECT))
+ goto normal_this;
+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {
+ val = JS_DupValue(ctx, ctx->global_obj);
+ } else {
+ val = JS_ToObject(ctx, this_obj);
+ if (JS_IsException(val))
+ goto exception;
+ }
+ } else {
+ normal_this:
+ val = JS_DupValue(ctx, this_obj);
+ }
+ *sp++ = val;
+ }
+ BREAK;
+ CASE(OP_push_false):
+ *sp++ = JS_FALSE;
+ BREAK;
+ CASE(OP_push_true):
+ *sp++ = JS_TRUE;
+ BREAK;
+ CASE(OP_object):
+ *sp++ = JS_NewObject(ctx);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ BREAK;
+ CASE(OP_special_object):
+ {
+ int arg = *pc++;
+ switch(arg) {
+ case OP_SPECIAL_OBJECT_ARGUMENTS:
+ *sp++ = js_build_arguments(ctx, argc, argv);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
+ *sp++ = js_build_mapped_arguments(ctx, argc, argv,
+ sf, min_int(argc, b->arg_count));
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ case OP_SPECIAL_OBJECT_THIS_FUNC:
+ *sp++ = JS_DupValue(ctx, sf->cur_func);
+ break;
+ case OP_SPECIAL_OBJECT_NEW_TARGET:
+ *sp++ = JS_DupValue(ctx, new_target);
+ break;
+ case OP_SPECIAL_OBJECT_HOME_OBJECT:
+ {
+ JSObject *p1;
+ p1 = p->u.func.home_object;
+ if (unlikely(!p1))
+ *sp++ = JS_UNDEFINED;
+ else
+ *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ }
+ break;
+ case OP_SPECIAL_OBJECT_VAR_OBJECT:
+ *sp++ = JS_NewObjectProto(ctx, JS_NULL);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ case OP_SPECIAL_OBJECT_IMPORT_META:
+ *sp++ = js_import_meta(ctx);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ default:
+ abort();
+ }
+ }
+ BREAK;
+ CASE(OP_rest):
+ {
+ int first = get_u16(pc);
+ pc += 2;
+ *sp++ = js_build_rest(ctx, first, argc, argv);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_drop):
+ JS_FreeValue(ctx, sp[-1]);
+ sp--;
+ BREAK;
+ CASE(OP_nip):
+ JS_FreeValue(ctx, sp[-2]);
+ sp[-2] = sp[-1];
+ sp--;
+ BREAK;
+ CASE(OP_nip1): /* a b c -> b c */
+ JS_FreeValue(ctx, sp[-3]);
+ sp[-3] = sp[-2];
+ sp[-2] = sp[-1];
+ sp--;
+ BREAK;
+ CASE(OP_dup):
+ sp[0] = JS_DupValue(ctx, sp[-1]);
+ sp++;
+ BREAK;
+ CASE(OP_dup2): /* a b -> a b a b */
+ sp[0] = JS_DupValue(ctx, sp[-2]);
+ sp[1] = JS_DupValue(ctx, sp[-1]);
+ sp += 2;
+ BREAK;
+ CASE(OP_dup3): /* a b c -> a b c a b c */
+ sp[0] = JS_DupValue(ctx, sp[-3]);
+ sp[1] = JS_DupValue(ctx, sp[-2]);
+ sp[2] = JS_DupValue(ctx, sp[-1]);
+ sp += 3;
+ BREAK;
+ CASE(OP_dup1): /* a b -> a a b */
+ sp[0] = sp[-1];
+ sp[-1] = JS_DupValue(ctx, sp[-2]);
+ sp++;
+ BREAK;
+ CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = JS_DupValue(ctx, sp[0]);
+ sp++;
+ BREAK;
+ CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = JS_DupValue(ctx, sp[0]);
+ sp++;
+ BREAK;
+ CASE(OP_insert4): /* this obj prop a -> a this obj prop a */
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = sp[-4];
+ sp[-4] = JS_DupValue(ctx, sp[0]);
+ sp++;
+ BREAK;
+ CASE(OP_perm3): /* obj a b -> a obj b (213) */
+ {
+ JSValue tmp;
+ tmp = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = tmp;
+ }
+ BREAK;
+ CASE(OP_rot3l): /* x a b -> a b x (231) */
+ {
+ JSValue tmp;
+ tmp = sp[-3];
+ sp[-3] = sp[-2];
+ sp[-2] = sp[-1];
+ sp[-1] = tmp;
+ }
+ BREAK;
+ CASE(OP_rot4l): /* x a b c -> a b c x */
+ {
+ JSValue tmp;
+ tmp = sp[-4];
+ sp[-4] = sp[-3];
+ sp[-3] = sp[-2];
+ sp[-2] = sp[-1];
+ sp[-1] = tmp;
+ }
+ BREAK;
+ CASE(OP_rot5l): /* x a b c d -> a b c d x */
+ {
+ JSValue tmp;
+ tmp = sp[-5];
+ sp[-5] = sp[-4];
+ sp[-4] = sp[-3];
+ sp[-3] = sp[-2];
+ sp[-2] = sp[-1];
+ sp[-1] = tmp;
+ }
+ BREAK;
+ CASE(OP_rot3r): /* a b x -> x a b (312) */
+ {
+ JSValue tmp;
+ tmp = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = tmp;
+ }
+ BREAK;
+ CASE(OP_perm4): /* obj prop a b -> a obj prop b */
+ {
+ JSValue tmp;
+ tmp = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = sp[-4];
+ sp[-4] = tmp;
+ }
+ BREAK;
+ CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */
+ {
+ JSValue tmp;
+ tmp = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = sp[-4];
+ sp[-4] = sp[-5];
+ sp[-5] = tmp;
+ }
+ BREAK;
+ CASE(OP_swap): /* a b -> b a */
+ {
+ JSValue tmp;
+ tmp = sp[-2];
+ sp[-2] = sp[-1];
+ sp[-1] = tmp;
+ }
+ BREAK;
+ CASE(OP_swap2): /* a b c d -> c d a b */
+ {
+ JSValue tmp1, tmp2;
+ tmp1 = sp[-4];
+ tmp2 = sp[-3];
+ sp[-4] = sp[-2];
+ sp[-3] = sp[-1];
+ sp[-2] = tmp1;
+ sp[-1] = tmp2;
+ }
+ BREAK;
+
+ CASE(OP_fclosure):
+ {
+ JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
+ pc += 4;
+ *sp++ = js_closure(ctx, bfunc, var_refs, sf);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ }
+ BREAK;
+#if SHORT_OPCODES
+ CASE(OP_call0):
+ CASE(OP_call1):
+ CASE(OP_call2):
+ CASE(OP_call3):
+ call_argc = opcode - OP_call0;
+ goto has_call_argc;
+#endif
+ CASE(OP_call):
+ CASE(OP_tail_call):
+ {
+ call_argc = get_u16(pc);
+ pc += 2;
+ goto has_call_argc;
+ has_call_argc:
+ call_argv = sp - call_argc;
+ sf->cur_pc = pc;
+ ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
+ JS_UNDEFINED, call_argc, call_argv, 0);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ if (opcode == OP_tail_call)
+ goto done;
+ for(i = -1; i < call_argc; i++)
+ JS_FreeValue(ctx, call_argv[i]);
+ sp -= call_argc + 1;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ CASE(OP_call_constructor):
+ {
+ call_argc = get_u16(pc);
+ pc += 2;
+ call_argv = sp - call_argc;
+ sf->cur_pc = pc;
+ ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],
+ call_argv[-1],
+ call_argc, call_argv, 0);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ for(i = -2; i < call_argc; i++)
+ JS_FreeValue(ctx, call_argv[i]);
+ sp -= call_argc + 2;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ CASE(OP_call_method):
+ CASE(OP_tail_call_method):
+ {
+ call_argc = get_u16(pc);
+ pc += 2;
+ call_argv = sp - call_argc;
+ sf->cur_pc = pc;
+ ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
+ JS_UNDEFINED, call_argc, call_argv, 0);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ if (opcode == OP_tail_call_method)
+ goto done;
+ for(i = -2; i < call_argc; i++)
+ JS_FreeValue(ctx, call_argv[i]);
+ sp -= call_argc + 2;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ CASE(OP_array_from):
+ {
+ int i, ret;
+
+ call_argc = get_u16(pc);
+ pc += 2;
+ ret_val = JS_NewArray(ctx);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ call_argv = sp - call_argc;
+ for(i = 0; i < call_argc; i++) {
+ ret = JS_DefinePropertyValue(ctx, ret_val, JS_AtomFromUInt32(i), call_argv[i],
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ call_argv[i] = JS_UNDEFINED;
+ if (ret < 0) {
+ JS_FreeValue(ctx, ret_val);
+ goto exception;
+ }
+ }
+ sp -= call_argc;
+ *sp++ = ret_val;
+ }
+ BREAK;
+
+ CASE(OP_apply):
+ {
+ int magic;
+ magic = get_u16(pc);
+ pc += 2;
+
+ ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-3]);
+ JS_FreeValue(ctx, sp[-2]);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 3;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ CASE(OP_return):
+ ret_val = *--sp;
+ goto done;
+ CASE(OP_return_undef):
+ ret_val = JS_UNDEFINED;
+ goto done;
+
+ CASE(OP_check_ctor_return):
+ /* return TRUE if 'this' should be returned */
+ if (!JS_IsObject(sp[-1])) {
+ if (!JS_IsUndefined(sp[-1])) {
+ JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");
+ goto exception;
+ }
+ sp[0] = JS_TRUE;
+ } else {
+ sp[0] = JS_FALSE;
+ }
+ sp++;
+ BREAK;
+ CASE(OP_check_ctor):
+ if (JS_IsUndefined(new_target)) {
+ JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'");
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_check_brand):
+ if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)
+ goto exception;
+ BREAK;
+ CASE(OP_add_brand):
+ if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
+ goto exception;
+ JS_FreeValue(ctx, sp[-2]);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 2;
+ BREAK;
+
+ CASE(OP_throw):
+ JS_Throw(ctx, *--sp);
+ goto exception;
+
+ CASE(OP_throw_error):
+#define JS_THROW_VAR_RO 0
+#define JS_THROW_VAR_REDECL 1
+#define JS_THROW_VAR_UNINITIALIZED 2
+#define JS_THROW_ERROR_DELETE_SUPER 3
+#define JS_THROW_ERROR_ITERATOR_THROW 4
+ {
+ JSAtom atom;
+ int type;
+ atom = get_u32(pc);
+ type = pc[4];
+ pc += 5;
+ if (type == JS_THROW_VAR_RO)
+ JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);
+ else
+ if (type == JS_THROW_VAR_REDECL)
+ JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);
+ else
+ if (type == JS_THROW_VAR_UNINITIALIZED)
+ JS_ThrowReferenceErrorUninitialized(ctx, atom);
+ else
+ if (type == JS_THROW_ERROR_DELETE_SUPER)
+ JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");
+ else
+ if (type == JS_THROW_ERROR_ITERATOR_THROW)
+ JS_ThrowTypeError(ctx, "iterator does not have a throw method");
+ else
+ JS_ThrowInternalError(ctx, "invalid throw var type %d", type);
+ }
+ goto exception;
+
+ CASE(OP_eval):
+ {
+ JSValueConst obj;
+ int scope_idx;
+ call_argc = get_u16(pc);
+ scope_idx = get_u16(pc + 2) - 1;
+ pc += 4;
+ call_argv = sp - call_argc;
+ sf->cur_pc = pc;
+ if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {
+ if (call_argc >= 1)
+ obj = call_argv[0];
+ else
+ obj = JS_UNDEFINED;
+ ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
+ JS_EVAL_TYPE_DIRECT, scope_idx);
+ } else {
+ ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
+ JS_UNDEFINED, call_argc, call_argv, 0);
+ }
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ for(i = -1; i < call_argc; i++)
+ JS_FreeValue(ctx, call_argv[i]);
+ sp -= call_argc + 1;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ /* could merge with OP_apply */
+ CASE(OP_apply_eval):
+ {
+ int scope_idx;
+ uint32_t len;
+ JSValue *tab;
+ JSValueConst obj;
+
+ scope_idx = get_u16(pc) - 1;
+ pc += 2;
+ tab = build_arg_list(ctx, &len, sp[-1]);
+ if (!tab)
+ goto exception;
+ if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {
+ if (len >= 1)
+ obj = tab[0];
+ else
+ obj = JS_UNDEFINED;
+ ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
+ JS_EVAL_TYPE_DIRECT, scope_idx);
+ } else {
+ ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
+ tab);
+ }
+ free_arg_list(ctx, tab, len);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-2]);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 2;
+ *sp++ = ret_val;
+ }
+ BREAK;
+
+ CASE(OP_regexp):
+ {
+ sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,
+ sp[-2], sp[-1]);
+ sp--;
+ }
+ BREAK;
+
+ CASE(OP_get_super):
+ {
+ JSValue proto;
+ proto = JS_GetPrototype(ctx, sp[-1]);
+ if (JS_IsException(proto))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = proto;
+ }
+ BREAK;
+
+ CASE(OP_import):
+ {
+ JSValue val;
+ val = js_dynamic_import(ctx, sp[-1]);
+ if (JS_IsException(val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = val;
+ }
+ BREAK;
+
+ CASE(OP_check_var):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_CheckGlobalVar(ctx, atom);
+ if (ret < 0)
+ goto exception;
+ *sp++ = JS_NewBool(ctx, ret);
+ }
+ BREAK;
+
+ CASE(OP_get_var_undef):
+ CASE(OP_get_var):
+ {
+ JSValue val;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ *sp++ = val;
+ }
+ BREAK;
+
+ CASE(OP_put_var):
+ CASE(OP_put_var_init):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);
+ sp--;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_put_var_strict):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ /* sp[-2] is JS_TRUE or JS_FALSE */
+ if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {
+ JS_ThrowReferenceErrorNotDefined(ctx, atom);
+ goto exception;
+ }
+ ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
+ sp -= 2;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_check_define_var):
+ {
+ JSAtom atom;
+ int flags;
+ atom = get_u32(pc);
+ flags = pc[4];
+ pc += 5;
+ if (JS_CheckDefineGlobalVar(ctx, atom, flags))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_define_var):
+ {
+ JSAtom atom;
+ int flags;
+ atom = get_u32(pc);
+ flags = pc[4];
+ pc += 5;
+ if (JS_DefineGlobalVar(ctx, atom, flags))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_define_func):
+ {
+ JSAtom atom;
+ int flags;
+ atom = get_u32(pc);
+ flags = pc[4];
+ pc += 5;
+ if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp--;
+ }
+ BREAK;
+
+ CASE(OP_get_loc):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ sp[0] = JS_DupValue(ctx, var_buf[idx]);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_loc):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &var_buf[idx], sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_loc):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
+ }
+ BREAK;
+ CASE(OP_get_arg):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ sp[0] = JS_DupValue(ctx, arg_buf[idx]);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_arg):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &arg_buf[idx], sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_arg):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));
+ }
+ BREAK;
+
+#if SHORT_OPCODES
+ CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;
+ CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;
+ CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;
+
+ CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;
+ CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;
+ CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;
+ CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;
+ CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;
+ CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;
+ CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;
+ CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;
+ CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;
+ CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;
+ CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;
+ CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;
+ CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;
+ CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;
+ CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;
+ CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;
+ CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;
+ CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;
+ CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;
+ CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;
+ CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;
+ CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;
+ CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;
+ CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;
+ CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
+#endif
+
+ CASE(OP_get_var_ref):
+ {
+ int idx;
+ JSValue val;
+ idx = get_u16(pc);
+ pc += 2;
+ val = *var_refs[idx]->pvalue;
+ sp[0] = JS_DupValue(ctx, val);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_var_ref):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_var_ref):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));
+ }
+ BREAK;
+ CASE(OP_get_var_ref_check):
+ {
+ int idx;
+ JSValue val;
+ idx = get_u16(pc);
+ pc += 2;
+ val = *var_refs[idx]->pvalue;
+ if (unlikely(JS_IsUninitialized(val))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
+ goto exception;
+ }
+ sp[0] = JS_DupValue(ctx, val);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_var_ref_check):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
+ goto exception;
+ }
+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_put_var_ref_check_init):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
+ goto exception;
+ }
+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_loc_uninitialized):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);
+ }
+ BREAK;
+ CASE(OP_get_loc_check):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
+ goto exception;
+ }
+ sp[0] = JS_DupValue(ctx, var_buf[idx]);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_loc_check):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
+ goto exception;
+ }
+ set_value(ctx, &var_buf[idx], sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_put_loc_check_init):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {
+ JS_ThrowReferenceError(ctx, "'this' can be initialized only once");
+ goto exception;
+ }
+ set_value(ctx, &var_buf[idx], sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_close_loc):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ close_lexical_var(ctx, sf, idx, FALSE);
+ }
+ BREAK;
+
+ CASE(OP_make_loc_ref):
+ CASE(OP_make_arg_ref):
+ CASE(OP_make_var_ref_ref):
+ {
+ JSVarRef *var_ref;
+ JSProperty *pr;
+ JSAtom atom;
+ int idx;
+ atom = get_u32(pc);
+ idx = get_u16(pc + 4);
+ pc += 6;
+ *sp++ = JS_NewObjectProto(ctx, JS_NULL);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ if (opcode == OP_make_var_ref_ref) {
+ var_ref = var_refs[idx];
+ var_ref->header.ref_count++;
+ } else {
+ var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
+ if (!var_ref)
+ goto exception;
+ }
+ pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,
+ JS_PROP_WRITABLE | JS_PROP_VARREF);
+ if (!pr) {
+ free_var_ref(rt, var_ref);
+ goto exception;
+ }
+ pr->u.var_ref = var_ref;
+ *sp++ = JS_AtomToValue(ctx, atom);
+ }
+ BREAK;
+ CASE(OP_make_var_ref):
+ {
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ if (JS_GetGlobalVarRef(ctx, atom, sp))
+ goto exception;
+ sp += 2;
+ }
+ BREAK;
+
+ CASE(OP_goto):
+ pc += (int32_t)get_u32(pc);
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ BREAK;
+#if SHORT_OPCODES
+ CASE(OP_goto16):
+ pc += (int16_t)get_u16(pc);
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ BREAK;
+ CASE(OP_goto8):
+ pc += (int8_t)pc[0];
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ BREAK;
+#endif
+ CASE(OP_if_true):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ pc += 4;
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1);
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp--;
+ if (res) {
+ pc += (int32_t)get_u32(pc - 4) - 4;
+ }
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_if_false):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ pc += 4;
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1);
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp--;
+ if (!res) {
+ pc += (int32_t)get_u32(pc - 4) - 4;
+ }
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ }
+ BREAK;
+#if SHORT_OPCODES
+ CASE(OP_if_true8):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ pc += 1;
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1);
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp--;
+ if (res) {
+ pc += (int8_t)pc[-1] - 1;
+ }
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_if_false8):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ pc += 1;
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1);
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp--;
+ if (!res) {
+ pc += (int8_t)pc[-1] - 1;
+ }
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ }
+ BREAK;
+#endif
+ CASE(OP_catch):
+ {
+ int32_t diff;
+ diff = get_u32(pc);
+ sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);
+ sp++;
+ pc += 4;
+ }
+ BREAK;
+ CASE(OP_gosub):
+ {
+ int32_t diff;
+ diff = get_u32(pc);
+ /* XXX: should have a different tag to avoid security flaw */
+ sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);
+ sp++;
+ pc += diff;
+ }
+ BREAK;
+ CASE(OP_ret):
+ {
+ JSValue op1;
+ uint32_t pos;
+ op1 = sp[-1];
+ if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))
+ goto ret_fail;
+ pos = JS_VALUE_GET_INT(op1);
+ if (unlikely(pos >= b->byte_code_len)) {
+ ret_fail:
+ JS_ThrowInternalError(ctx, "invalid ret value");
+ goto exception;
+ }
+ sp--;
+ pc = b->byte_code_buf + pos;
+ }
+ BREAK;
+
+ CASE(OP_for_in_start):
+ if (js_for_in_start(ctx, sp))
+ goto exception;
+ BREAK;
+ CASE(OP_for_in_next):
+ if (js_for_in_next(ctx, sp))
+ goto exception;
+ sp += 2;
+ BREAK;
+ CASE(OP_for_of_start):
+ if (js_for_of_start(ctx, sp, FALSE))
+ goto exception;
+ sp += 1;
+ *sp++ = JS_NewCatchOffset(ctx, 0);
+ BREAK;
+ CASE(OP_for_of_next):
+ {
+ int offset = -3 - pc[0];
+ pc += 1;
+ if (js_for_of_next(ctx, sp, offset))
+ goto exception;
+ sp += 2;
+ }
+ BREAK;
+ CASE(OP_for_await_of_start):
+ if (js_for_of_start(ctx, sp, TRUE))
+ goto exception;
+ sp += 1;
+ *sp++ = JS_NewCatchOffset(ctx, 0);
+ BREAK;
+ CASE(OP_iterator_get_value_done):
+ if (js_iterator_get_value_done(ctx, sp))
+ goto exception;
+ sp += 1;
+ BREAK;
+ CASE(OP_iterator_check_object):
+ if (unlikely(!JS_IsObject(sp[-1]))) {
+ JS_ThrowTypeError(ctx, "iterator must return an object");
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_iterator_close):
+ /* iter_obj next catch_offset -> */
+ sp--; /* drop the catch offset to avoid getting caught by exception */
+ JS_FreeValue(ctx, sp[-1]); /* drop the next method */
+ sp--;
+ if (!JS_IsUndefined(sp[-1])) {
+ if (JS_IteratorClose(ctx, sp[-1], FALSE))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ }
+ sp--;
+ BREAK;
+ CASE(OP_iterator_close_return):
+ {
+ JSValue ret_val;
+ /* iter_obj next catch_offset ... ret_val ->
+ ret_eval iter_obj next catch_offset */
+ ret_val = *--sp;
+ while (sp > stack_buf &&
+ JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
+ JS_FreeValue(ctx, *--sp);
+ }
+ if (unlikely(sp < stack_buf + 3)) {
+ JS_ThrowInternalError(ctx, "iterator_close_return");
+ JS_FreeValue(ctx, ret_val);
+ goto exception;
+ }
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = ret_val;
+ sp++;
+ }
+ BREAK;
+
+ CASE(OP_iterator_next):
+ /* stack: iter_obj next catch_offset val */
+ {
+ JSValue ret;
+ ret = JS_Call(ctx, sp[-3], sp[-4],
+ 1, (sp - 1));
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret;
+ }
+ BREAK;
+
+ CASE(OP_iterator_call):
+ /* stack: iter_obj next catch_offset val */
+ {
+ JSValue method, ret;
+ BOOL ret_flag;
+ int flags;
+ flags = *pc++;
+ method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?
+ JS_ATOM_throw : JS_ATOM_return);
+ if (JS_IsException(method))
+ goto exception;
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
+ ret_flag = TRUE;
+ } else {
+ if (flags & 2) {
+ /* no argument */
+ ret = JS_CallFree(ctx, method, sp[-4],
+ 0, NULL);
+ } else {
+ ret = JS_CallFree(ctx, method, sp[-4],
+ 1, (sp - 1));
+ }
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret;
+ ret_flag = FALSE;
+ }
+ sp[0] = JS_NewBool(ctx, ret_flag);
+ sp += 1;
+ }
+ BREAK;
+
+ CASE(OP_lnot):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1) != 0;
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp[-1] = JS_NewBool(ctx, !res);
+ }
+ BREAK;
+
+ CASE(OP_get_field):
+ {
+ JSValue val;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ val = JS_GetProperty(ctx, sp[-1], atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = val;
+ }
+ BREAK;
+
+ CASE(OP_get_field2):
+ {
+ JSValue val;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ val = JS_GetProperty(ctx, sp[-1], atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ *sp++ = val;
+ }
+ BREAK;
+
+ CASE(OP_put_field):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],
+ JS_PROP_THROW_STRICT);
+ JS_FreeValue(ctx, sp[-2]);
+ sp -= 2;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_private_symbol):
+ {
+ JSAtom atom;
+ JSValue val;
+
+ atom = get_u32(pc);
+ pc += 4;
+ val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);
+ if (JS_IsException(val))
+ goto exception;
+ *sp++ = val;
+ }
+ BREAK;
+
+ CASE(OP_get_private_field):
+ {
+ JSValue val;
+
+ val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);
+ JS_FreeValue(ctx, sp[-1]);
+ JS_FreeValue(ctx, sp[-2]);
+ sp[-2] = val;
+ sp--;
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_put_private_field):
+ {
+ int ret;
+ ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);
+ JS_FreeValue(ctx, sp[-3]);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 3;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_define_private_field):
+ {
+ int ret;
+ ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);
+ JS_FreeValue(ctx, sp[-2]);
+ sp -= 2;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_define_field):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ sp--;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_set_name):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_set_name_computed):
+ {
+ int ret;
+ ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_set_proto):
+ {
+ JSValue proto;
+ proto = sp[-1];
+ if (JS_IsObject(proto) || JS_IsNull(proto)) {
+ if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)
+ goto exception;
+ }
+ JS_FreeValue(ctx, proto);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_home_object):
+ js_method_set_home_object(ctx, sp[-1], sp[-2]);
+ BREAK;
+ CASE(OP_define_method):
+ CASE(OP_define_method_computed):
+ {
+ JSValue getter, setter, value;
+ JSValueConst obj;
+ JSAtom atom;
+ int flags, ret, op_flags;
+ BOOL is_computed;
+#define OP_DEFINE_METHOD_METHOD 0
+#define OP_DEFINE_METHOD_GETTER 1
+#define OP_DEFINE_METHOD_SETTER 2
+#define OP_DEFINE_METHOD_ENUMERABLE 4
+
+ is_computed = (opcode == OP_define_method_computed);
+ if (is_computed) {
+ atom = JS_ValueToAtom(ctx, sp[-2]);
+ if (unlikely(atom == JS_ATOM_NULL))
+ goto exception;
+ opcode += OP_define_method - OP_define_method_computed;
+ } else {
+ atom = get_u32(pc);
+ pc += 4;
+ }
+ op_flags = *pc++;
+
+ obj = sp[-2 - is_computed];
+ flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |
+ JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;
+ if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)
+ flags |= JS_PROP_ENUMERABLE;
+ op_flags &= 3;
+ value = JS_UNDEFINED;
+ getter = JS_UNDEFINED;
+ setter = JS_UNDEFINED;
+ if (op_flags == OP_DEFINE_METHOD_METHOD) {
+ value = sp[-1];
+ flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;
+ } else if (op_flags == OP_DEFINE_METHOD_GETTER) {
+ getter = sp[-1];
+ flags |= JS_PROP_HAS_GET;
+ } else {
+ setter = sp[-1];
+ flags |= JS_PROP_HAS_SET;
+ }
+ ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);
+ if (ret >= 0) {
+ ret = JS_DefineProperty(ctx, obj, atom, value,
+ getter, setter, flags);
+ }
+ JS_FreeValue(ctx, sp[-1]);
+ if (is_computed) {
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, sp[-2]);
+ }
+ sp -= 1 + is_computed;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_define_class):
+ CASE(OP_define_class_computed):
+ {
+ int class_flags;
+ JSAtom atom;
+
+ atom = get_u32(pc);
+ class_flags = pc[4];
+ pc += 5;
+ if (js_op_define_class(ctx, sp, atom, class_flags,
+ var_refs, sf,
+ (opcode == OP_define_class_computed)) < 0)
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_get_array_el):
+ {
+ JSValue val;
+
+ val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
+ JS_FreeValue(ctx, sp[-2]);
+ sp[-2] = val;
+ sp--;
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_get_array_el2):
+ {
+ JSValue val;
+
+ val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
+ sp[-1] = val;
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_get_ref_value):
+ {
+ JSValue val;
+ if (unlikely(JS_IsUndefined(sp[-2]))) {
+ JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);
+ if (atom != JS_ATOM_NULL) {
+ JS_ThrowReferenceErrorNotDefined(ctx, atom);
+ JS_FreeAtom(ctx, atom);
+ }
+ goto exception;
+ }
+ val = JS_GetPropertyValue(ctx, sp[-2],
+ JS_DupValue(ctx, sp[-1]));
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ sp[0] = val;
+ sp++;
+ }
+ BREAK;
+
+ CASE(OP_get_super_value):
+ {
+ JSValue val;
+ JSAtom atom;
+ atom = JS_ValueToAtom(ctx, sp[-1]);
+ if (unlikely(atom == JS_ATOM_NULL))
+ goto exception;
+ val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
+ JS_FreeAtom(ctx, atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ JS_FreeValue(ctx, sp[-2]);
+ JS_FreeValue(ctx, sp[-3]);
+ sp[-3] = val;
+ sp -= 2;
+ }
+ BREAK;
+
+ CASE(OP_put_array_el):
+ {
+ int ret;
+
+ ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
+ JS_FreeValue(ctx, sp[-3]);
+ sp -= 3;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_put_ref_value):
+ {
+ int ret, flags;
+ flags = JS_PROP_THROW_STRICT;
+ if (unlikely(JS_IsUndefined(sp[-3]))) {
+ if (is_strict_mode(ctx)) {
+ JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);
+ if (atom != JS_ATOM_NULL) {
+ JS_ThrowReferenceErrorNotDefined(ctx, atom);
+ JS_FreeAtom(ctx, atom);
+ }
+ goto exception;
+ } else {
+ sp[-3] = JS_DupValue(ctx, ctx->global_obj);
+ }
+ } else {
+ if (is_strict_mode(ctx))
+ flags |= JS_PROP_NO_ADD;
+ }
+ ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags);
+ JS_FreeValue(ctx, sp[-3]);
+ sp -= 3;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_put_super_value):
+ {
+ int ret;
+ JSAtom atom;
+ if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto exception;
+ }
+ atom = JS_ValueToAtom(ctx, sp[-2]);
+ if (unlikely(atom == JS_ATOM_NULL))
+ goto exception;
+ ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4],
+ JS_PROP_THROW_STRICT);
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, sp[-4]);
+ JS_FreeValue(ctx, sp[-3]);
+ JS_FreeValue(ctx, sp[-2]);
+ sp -= 4;
+ if (ret < 0)
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_define_array_el):
+ {
+ int ret;
+ ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ sp -= 1;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_append): /* array pos enumobj -- array pos */
+ {
+ if (js_append_enumerate(ctx, sp))
+ goto exception;
+ JS_FreeValue(ctx, *--sp);
+ }
+ BREAK;
+
+ CASE(OP_copy_data_properties): /* target source excludeList */
+ {
+ /* stack offsets (-1 based):
+ 2 bits for target,
+ 3 bits for source,
+ 2 bits for exclusionList */
+ int mask;
+
+ mask = *pc++;
+ if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],
+ sp[-1 - ((mask >> 2) & 7)],
+ sp[-1 - ((mask >> 5) & 7)], 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_add):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int64_t r;
+ r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);
+ if (unlikely((int)r != r))
+ goto add_slow;
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) +
+ JS_VALUE_GET_FLOAT64(op2));
+ sp--;
+ } else {
+ add_slow:
+ if (js_add_slow(ctx, sp))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_add_loc):
+ {
+ JSValue *pv;
+ int idx;
+ idx = *pc;
+ pc += 1;
+
+ pv = &var_buf[idx];
+ if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) {
+ int64_t r;
+ r = (int64_t)JS_VALUE_GET_INT(*pv) +
+ JS_VALUE_GET_INT(sp[-1]);
+ if (unlikely((int)r != r))
+ goto add_loc_slow;
+ *pv = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {
+ JSValue op1;
+ op1 = sp[-1];
+ sp--;
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1))
+ goto exception;
+ op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);
+ if (JS_IsException(op1))
+ goto exception;
+ set_value(ctx, pv, op1);
+ } else {
+ JSValue ops[2];
+ add_loc_slow:
+ /* In case of exception, js_add_slow frees ops[0]
+ and ops[1], so we must duplicate *pv */
+ ops[0] = JS_DupValue(ctx, *pv);
+ ops[1] = sp[-1];
+ sp--;
+ if (js_add_slow(ctx, ops + 2))
+ goto exception;
+ set_value(ctx, pv, ops[0]);
+ }
+ }
+ BREAK;
+ CASE(OP_sub):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int64_t r;
+ r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);
+ if (unlikely((int)r != r))
+ goto binary_arith_slow;
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) -
+ JS_VALUE_GET_FLOAT64(op2));
+ sp--;
+ } else {
+ goto binary_arith_slow;
+ }
+ }
+ BREAK;
+ CASE(OP_mul):
+ {
+ JSValue op1, op2;
+ double d;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int32_t v1, v2;
+ int64_t r;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ r = (int64_t)v1 * v2;
+ if (unlikely((int)r != r)) {
+#ifdef CONFIG_BIGNUM
+ if (unlikely(sf->js_mode & JS_MODE_MATH) &&
+ (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
+ goto binary_arith_slow;
+#endif
+ d = (double)r;
+ goto mul_fp_res;
+ }
+ /* need to test zero case for -0 result */
+ if (unlikely(r == 0 && (v1 | v2) < 0)) {
+ d = -0.0;
+ goto mul_fp_res;
+ }
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+#ifdef CONFIG_BIGNUM
+ if (unlikely(sf->js_mode & JS_MODE_MATH))
+ goto binary_arith_slow;
+#endif
+ d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
+ mul_fp_res:
+ sp[-2] = JS_NewFloat64Impl(ctx, d);
+ sp--;
+ } else {
+ goto binary_arith_slow;
+ }
+ }
+ BREAK;
+ CASE(OP_div):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int v1, v2;
+ if (unlikely(sf->js_mode & JS_MODE_MATH))
+ goto binary_arith_slow;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
+ sp--;
+ } else {
+ goto binary_arith_slow;
+ }
+ }
+ BREAK;
+ CASE(OP_mod):
+#ifdef CONFIG_BIGNUM
+ CASE(OP_math_mod):
+#endif
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int v1, v2, r;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =
+ -1 and the cases where the result is -0. */
+ if (unlikely(v1 < 0 || v2 <= 0))
+ goto binary_arith_slow;
+ r = v1 % v2;
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else {
+ goto binary_arith_slow;
+ }
+ }
+ BREAK;
+ CASE(OP_pow):
+ binary_arith_slow:
+ if (js_binary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ BREAK;
+
+ CASE(OP_plus):
+ {
+ JSValue op1;
+ uint32_t tag;
+ op1 = sp[-1];
+ tag = JS_VALUE_GET_TAG(op1);
+ if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {
+ } else {
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ }
+ }
+ BREAK;
+ CASE(OP_neg):
+ {
+ JSValue op1;
+ uint32_t tag;
+ int val;
+ double d;
+ op1 = sp[-1];
+ tag = JS_VALUE_GET_TAG(op1);
+ if (tag == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ /* Note: -0 cannot be expressed as integer */
+ if (unlikely(val == 0)) {
+ d = -0.0;
+ goto neg_fp_res;
+ }
+ if (unlikely(val == INT32_MIN)) {
+ d = -(double)val;
+ goto neg_fp_res;
+ }
+ sp[-1] = JS_NewInt32(ctx, -val);
+ } else if (JS_TAG_IS_FLOAT64(tag)) {
+ d = -JS_VALUE_GET_FLOAT64(op1);
+ neg_fp_res:
+ sp[-1] = JS_NewFloat64Impl(ctx, d);
+ } else {
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ }
+ }
+ BREAK;
+ CASE(OP_inc):
+ {
+ JSValue op1;
+ int val;
+ op1 = sp[-1];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ if (unlikely(val == INT32_MAX))
+ goto inc_slow;
+ sp[-1] = JS_NewInt32(ctx, val + 1);
+ } else {
+ inc_slow:
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ }
+ }
+ BREAK;
+ CASE(OP_dec):
+ {
+ JSValue op1;
+ int val;
+ op1 = sp[-1];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ if (unlikely(val == INT32_MIN))
+ goto dec_slow;
+ sp[-1] = JS_NewInt32(ctx, val - 1);
+ } else {
+ dec_slow:
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ }
+ }
+ BREAK;
+ CASE(OP_post_inc):
+ CASE(OP_post_dec):
+ if (js_post_inc_slow(ctx, sp, opcode))
+ goto exception;
+ sp++;
+ BREAK;
+ CASE(OP_inc_loc):
+ {
+ JSValue op1;
+ int val;
+ int idx;
+ idx = *pc;
+ pc += 1;
+
+ op1 = var_buf[idx];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ if (unlikely(val == INT32_MAX))
+ goto inc_loc_slow;
+ var_buf[idx] = JS_NewInt32(ctx, val + 1);
+ } else {
+ inc_loc_slow:
+ /* must duplicate otherwise the variable value may
+ be destroyed before JS code accesses it */
+ op1 = JS_DupValue(ctx, op1);
+ if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))
+ goto exception;
+ set_value(ctx, &var_buf[idx], op1);
+ }
+ }
+ BREAK;
+ CASE(OP_dec_loc):
+ {
+ JSValue op1;
+ int val;
+ int idx;
+ idx = *pc;
+ pc += 1;
+
+ op1 = var_buf[idx];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ if (unlikely(val == INT32_MIN))
+ goto dec_loc_slow;
+ var_buf[idx] = JS_NewInt32(ctx, val - 1);
+ } else {
+ dec_loc_slow:
+ /* must duplicate otherwise the variable value may
+ be destroyed before JS code accesses it */
+ op1 = JS_DupValue(ctx, op1);
+ if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))
+ goto exception;
+ set_value(ctx, &var_buf[idx], op1);
+ }
+ }
+ BREAK;
+ CASE(OP_not):
+ {
+ JSValue op1;
+ op1 = sp[-1];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
+ } else {
+ if (js_not_slow(ctx, sp))
+ goto exception;
+ }
+ }
+ BREAK;
+
+ CASE(OP_shl):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ uint32_t v1, v2;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+#ifdef CONFIG_BIGNUM
+ {
+ int64_t r;
+ if (unlikely(sf->js_mode & JS_MODE_MATH)) {
+ if (v2 > 0x1f)
+ goto shl_slow;
+ r = (int64_t)v1 << v2;
+ if ((int)r != r)
+ goto shl_slow;
+ } else {
+ v2 &= 0x1f;
+ }
+ }
+#else
+ v2 &= 0x1f;
+#endif
+ sp[-2] = JS_NewInt32(ctx, v1 << v2);
+ sp--;
+ } else {
+#ifdef CONFIG_BIGNUM
+ shl_slow:
+#endif
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_shr):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ uint32_t v2;
+ v2 = JS_VALUE_GET_INT(op2);
+ /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */
+ v2 &= 0x1f;
+ sp[-2] = JS_NewUint32(ctx,
+ (uint32_t)JS_VALUE_GET_INT(op1) >>
+ v2);
+ sp--;
+ } else {
+ if (js_shr_slow(ctx, sp))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_sar):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ uint32_t v2;
+ v2 = JS_VALUE_GET_INT(op2);
+#ifdef CONFIG_BIGNUM
+ if (unlikely(v2 > 0x1f)) {
+ if (unlikely(sf->js_mode & JS_MODE_MATH))
+ goto sar_slow;
+ else
+ v2 &= 0x1f;
+ }
+#else
+ v2 &= 0x1f;
+#endif
+ sp[-2] = JS_NewInt32(ctx,
+ (int)JS_VALUE_GET_INT(op1) >> v2);
+ sp--;
+ } else {
+#ifdef CONFIG_BIGNUM
+ sar_slow:
+#endif
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_and):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ sp[-2] = JS_NewInt32(ctx,
+ JS_VALUE_GET_INT(op1) &
+ JS_VALUE_GET_INT(op2));
+ sp--;
+ } else {
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_or):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ sp[-2] = JS_NewInt32(ctx,
+ JS_VALUE_GET_INT(op1) |
+ JS_VALUE_GET_INT(op2));
+ sp--;
+ } else {
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_xor):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ sp[-2] = JS_NewInt32(ctx,
+ JS_VALUE_GET_INT(op1) ^
+ JS_VALUE_GET_INT(op2));
+ sp--;
+ } else {
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+
+
+#define OP_CMP(opcode, binary_op, slow_call) \
+ CASE(opcode): \
+ { \
+ JSValue op1, op2; \
+ op1 = sp[-2]; \
+ op2 = sp[-1]; \
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \
+ sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \
+ sp--; \
+ } else { \
+ if (slow_call) \
+ goto exception; \
+ sp--; \
+ } \
+ } \
+ BREAK
+
+ OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));
+ OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
+ OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
+ OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
+ OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));
+ OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));
+ OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
+ OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
+
+#ifdef CONFIG_BIGNUM
+ CASE(OP_mul_pow10):
+ if (rt->bigfloat_ops.mul_pow10(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
+#endif
+ CASE(OP_in):
+ if (js_operator_in(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
+ CASE(OP_instanceof):
+ if (js_operator_instanceof(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
+ CASE(OP_typeof):
+ {
+ JSValue op1;
+ JSAtom atom;
+
+ op1 = sp[-1];
+ atom = js_operator_typeof(ctx, op1);
+ JS_FreeValue(ctx, op1);
+ sp[-1] = JS_AtomToString(ctx, atom);
+ }
+ BREAK;
+ CASE(OP_delete):
+ if (js_operator_delete(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
+ CASE(OP_delete_var):
+ {
+ JSAtom atom;
+ int ret;
+
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);
+ if (unlikely(ret < 0))
+ goto exception;
+ *sp++ = JS_NewBool(ctx, ret);
+ }
+ BREAK;
+
+ CASE(OP_to_object):
+ if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {
+ ret_val = JS_ToObject(ctx, sp[-1]);
+ if (JS_IsException(ret_val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret_val;
+ }
+ BREAK;
+
+ CASE(OP_to_propkey):
+ switch (JS_VALUE_GET_TAG(sp[-1])) {
+ case JS_TAG_INT:
+ case JS_TAG_STRING:
+ case JS_TAG_SYMBOL:
+ break;
+ default:
+ ret_val = JS_ToPropertyKey(ctx, sp[-1]);
+ if (JS_IsException(ret_val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret_val;
+ break;
+ }
+ BREAK;
+
+ CASE(OP_to_propkey2):
+ /* must be tested first */
+ if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {
+ JS_ThrowTypeError(ctx, "value has no property");
+ goto exception;
+ }
+ switch (JS_VALUE_GET_TAG(sp[-1])) {
+ case JS_TAG_INT:
+ case JS_TAG_STRING:
+ case JS_TAG_SYMBOL:
+ break;
+ default:
+ ret_val = JS_ToPropertyKey(ctx, sp[-1]);
+ if (JS_IsException(ret_val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret_val;
+ break;
+ }
+ BREAK;
+#if 0
+ CASE(OP_to_string):
+ if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {
+ ret_val = JS_ToString(ctx, sp[-1]);
+ if (JS_IsException(ret_val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret_val;
+ }
+ BREAK;
+#endif
+ CASE(OP_with_get_var):
+ CASE(OP_with_put_var):
+ CASE(OP_with_delete_var):
+ CASE(OP_with_make_ref):
+ CASE(OP_with_get_ref):
+ CASE(OP_with_get_ref_undef):
+ {
+ JSAtom atom;
+ int32_t diff;
+ JSValue obj, val;
+ int ret, is_with;
+ atom = get_u32(pc);
+ diff = get_u32(pc + 4);
+ is_with = pc[8];
+ pc += 9;
+
+ obj = sp[-1];
+ ret = JS_HasProperty(ctx, obj, atom);
+ if (unlikely(ret < 0))
+ goto exception;
+ if (ret) {
+ if (is_with) {
+ ret = js_has_unscopable(ctx, obj, atom);
+ if (unlikely(ret < 0))
+ goto exception;
+ if (ret)
+ goto no_with;
+ }
+ switch (opcode) {
+ case OP_with_get_var:
+ val = JS_GetProperty(ctx, obj, atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ set_value(ctx, &sp[-1], val);
+ break;
+ case OP_with_put_var:
+ /* XXX: check if strict mode */
+ ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],
+ JS_PROP_THROW_STRICT);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 2;
+ if (unlikely(ret < 0))
+ goto exception;
+ break;
+ case OP_with_delete_var:
+ ret = JS_DeleteProperty(ctx, obj, atom, 0);
+ if (unlikely(ret < 0))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = JS_NewBool(ctx, ret);
+ break;
+ case OP_with_make_ref:
+ /* produce a pair object/propname on the stack */
+ *sp++ = JS_AtomToValue(ctx, atom);
+ break;
+ case OP_with_get_ref:
+ /* produce a pair object/method on the stack */
+ val = JS_GetProperty(ctx, obj, atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ *sp++ = val;
+ break;
+ case OP_with_get_ref_undef:
+ /* produce a pair undefined/function on the stack */
+ val = JS_GetProperty(ctx, obj, atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = JS_UNDEFINED;
+ *sp++ = val;
+ break;
+ }
+ pc += diff - 5;
+ } else {
+ no_with:
+ /* if not jumping, drop the object argument */
+ JS_FreeValue(ctx, sp[-1]);
+ sp--;
+ }
+ }
+ BREAK;
+
+ CASE(OP_await):
+ ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);
+ goto done_generator;
+ CASE(OP_yield):
+ ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);
+ goto done_generator;
+ CASE(OP_yield_star):
+ CASE(OP_async_yield_star):
+ ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
+ goto done_generator;
+ CASE(OP_return_async):
+ CASE(OP_initial_yield):
+ ret_val = JS_UNDEFINED;
+ goto done_generator;
+
+ CASE(OP_nop):
+ BREAK;
+ CASE(OP_is_undefined_or_null):
+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||
+ JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
+ goto set_true;
+ } else {
+ goto free_and_set_false;
+ }
+#if SHORT_OPCODES
+ CASE(OP_is_undefined):
+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {
+ goto set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ CASE(OP_is_null):
+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
+ goto set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ /* XXX: could merge to a single opcode */
+ CASE(OP_typeof_is_undefined):
+ /* different from OP_is_undefined because of isHTMLDDA */
+ if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
+ goto free_and_set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ CASE(OP_typeof_is_function):
+ if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
+ goto free_and_set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ free_and_set_true:
+ JS_FreeValue(ctx, sp[-1]);
+#endif
+ set_true:
+ sp[-1] = JS_TRUE;
+ BREAK;
+ free_and_set_false:
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = JS_FALSE;
+ BREAK;
+ CASE(OP_invalid):
+ DEFAULT:
+ JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",
+ (int)(pc - b->byte_code_buf - 1), opcode);
+ goto exception;
+ }
+ }
+ exception:
+ if (JS_IsString(rt->current_exception)) {
+ JSValue error_obj = JS_NewError(ctx);
+ JSAtom msgProp = JS_NewAtom(ctx, "message");
+ JS_DefinePropertyValue(ctx, error_obj, msgProp, rt->current_exception, 0);
+ rt->current_exception = error_obj;
+ JS_FreeAtom(ctx, msgProp);
+ }
+ if (is_backtrace_needed(ctx, rt->current_exception)) {
+ /* add the backtrace information now (it is not done
+ before if the exception happens in a bytecode
+ operation */
+ sf->cur_pc = pc;
+ build_backtrace(ctx, rt->current_exception, NULL, 0, 0);
+ }
+ if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
+ while (sp > stack_buf) {
+ JSValue val = *--sp;
+ JS_FreeValue(ctx, val);
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {
+ int pos = JS_VALUE_GET_INT(val);
+ if (pos == 0) {
+ /* enumerator: close it with a throw */
+ JS_FreeValue(ctx, sp[-1]); /* drop the next method */
+ sp--;
+ JS_IteratorClose(ctx, sp[-1], TRUE);
+ } else {
+ *sp++ = rt->current_exception;
+ rt->current_exception = JS_NULL;
+ pc = b->byte_code_buf + pos;
+ goto restart;
+ }
+ }
+ }
+ }
+ ret_val = JS_EXCEPTION;
+ /* the local variables are freed by the caller in the generator
+ case. Hence the label 'done' should never be reached in a
+ generator function. */
+ if (b->func_kind != JS_FUNC_NORMAL) {
+ done_generator:
+ sf->cur_pc = pc;
+ sf->cur_sp = sp;
+ } else {
+ done:
+ if (unlikely(!list_empty(&sf->var_ref_list))) {
+ /* variable references reference the stack: must close them */
+ close_var_refs(rt, sf);
+ }
+ /* free the local variables and stack */
+ for(pval = local_buf; pval < sp; pval++) {
+ JS_FreeValue(ctx, *pval);
+ }
+ }
+ rt->current_stack_frame = sf->prev_frame;
+ if (ctx->handleFunctionExited)
+ ctx->handleFunctionExited(ctx);
+ return ret_val;
+}
+
+JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
+ argc, argv, JS_CALL_FLAG_COPY_ARGV);
+}
+
+static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
+ argc, argv, JS_CALL_FLAG_COPY_ARGV);
+ JS_FreeValue(ctx, func_obj);
+ return res;
+}
+
+/* warning: the refcount of the context is not incremented. Return
+ NULL in case of exception (case of revoked proxy only) */
+static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)
+{
+ JSObject *p;
+ JSContext *realm;
+
+ if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
+ return ctx;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ switch(p->class_id) {
+ case JS_CLASS_C_FUNCTION:
+ realm = p->u.cfunc.realm;
+ break;
+ case JS_CLASS_BYTECODE_FUNCTION:
+ case JS_CLASS_GENERATOR_FUNCTION:
+ case JS_CLASS_ASYNC_FUNCTION:
+ case JS_CLASS_ASYNC_GENERATOR_FUNCTION:
+ {
+ JSFunctionBytecode *b;
+ b = p->u.func.function_bytecode;
+ realm = b->realm;
+ }
+ break;
+ case JS_CLASS_PROXY:
+ {
+ JSProxyData *s = p->u.opaque;
+ if (!s)
+ return ctx;
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ return NULL;
+ } else {
+ realm = JS_GetFunctionRealm(ctx, s->target);
+ }
+ }
+ break;
+ case JS_CLASS_BOUND_FUNCTION:
+ {
+ JSBoundFunction *bf = p->u.bound_function;
+ realm = JS_GetFunctionRealm(ctx, bf->func_obj);
+ }
+ break;
+ default:
+ realm = ctx;
+ break;
+ }
+ return realm;
+}
+
+static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
+ int class_id)
+{
+ JSValue proto, obj;
+ JSContext *realm;
+
+ if (JS_IsUndefined(ctor)) {
+ proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
+ } else {
+ proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
+ if (JS_IsException(proto))
+ return proto;
+ if (!JS_IsObject(proto)) {
+ JS_FreeValue(ctx, proto);
+ realm = JS_GetFunctionRealm(ctx, ctor);
+ if (!realm)
+ return JS_EXCEPTION;
+ proto = JS_DupValue(ctx, realm->class_proto[class_id]);
+ }
+ }
+ obj = JS_NewObjectProtoClass(ctx, proto, class_id);
+ JS_FreeValue(ctx, proto);
+ return obj;
+}
+
+/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
+static JSValue JS_CallConstructorInternal(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValue *argv, int flags)
+{
+ JSObject *p;
+ JSFunctionBytecode *b;
+
+ if (js_poll_interrupts(ctx))
+ return JS_EXCEPTION;
+ flags |= JS_CALL_FLAG_CONSTRUCTOR;
+ if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))
+ goto not_a_function;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ if (unlikely(!p->is_constructor))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
+ JSClassCall *call_func;
+ call_func = ctx->rt->class_array[p->class_id].call;
+ if (!call_func) {
+ not_a_function:
+ return JS_ThrowTypeError(ctx, "not a function");
+ }
+ return call_func(ctx, func_obj, new_target, argc,
+ argv, flags);
+ }
+
+ b = p->u.func.function_bytecode;
+ if (b->is_derived_class_constructor) {
+ return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);
+ } else {
+ JSValue obj, ret;
+ /* legacy constructor behavior */
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);
+ if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||
+ JS_IsException(ret)) {
+ JS_FreeValue(ctx, obj);
+ return ret;
+ } else {
+ JS_FreeValue(ctx, ret);
+ return obj;
+ }
+ }
+}
+
+JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ return JS_CallConstructorInternal(ctx, func_obj, new_target,
+ argc, argv,
+ JS_CALL_FLAG_COPY_ARGV);
+}
+
+JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
+ int argc, JSValueConst *argv)
+{
+ return JS_CallConstructorInternal(ctx, func_obj, func_obj,
+ argc, argv,
+ JS_CALL_FLAG_COPY_ARGV);
+}
+
+JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
+ int argc, JSValueConst *argv)
+{
+ JSValue func_obj;
+ func_obj = JS_GetProperty(ctx, this_val, atom);
+ if (JS_IsException(func_obj))
+ return func_obj;
+ return JS_CallFree(ctx, func_obj, this_val, argc, argv);
+}
+
+static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
+ int argc, JSValueConst *argv)
+{
+ JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);
+ JS_FreeValue(ctx, this_val);
+ return res;
+}
+
+/* JSAsyncFunctionState (used by generator and async functions) */
+static warn_unused int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
+ JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ JSFunctionBytecode *b;
+ JSStackFrame *sf;
+ int local_count, i, arg_buf_len, n;
+
+ sf = &s->frame;
+ init_list_head(&sf->var_ref_list);
+ p = JS_VALUE_GET_OBJ(func_obj);
+ b = p->u.func.function_bytecode;
+ sf->js_mode = b->js_mode;
+ sf->cur_pc = b->byte_code_buf;
+ arg_buf_len = max_int(b->arg_count, argc);
+ local_count = arg_buf_len + b->var_count + b->stack_size;
+ sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
+ if (!sf->arg_buf)
+ return -1;
+ sf->cur_func = JS_DupValue(ctx, func_obj);
+ s->this_val = JS_DupValue(ctx, this_obj);
+ s->argc = argc;
+ sf->arg_count = arg_buf_len;
+ sf->var_buf = sf->arg_buf + arg_buf_len;
+ sf->cur_sp = sf->var_buf + b->var_count;
+ for(i = 0; i < argc; i++)
+ sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
+ n = arg_buf_len + b->var_count;
+ for(i = argc; i < n; i++)
+ sf->arg_buf[i] = JS_UNDEFINED;
+ return 0;
+}
+
+static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
+ JS_MarkFunc *mark_func)
+{
+ JSStackFrame *sf;
+ JSValue *sp;
+
+ sf = &s->frame;
+ JS_MarkValue(rt, sf->cur_func, mark_func);
+ JS_MarkValue(rt, s->this_val, mark_func);
+ if (sf->cur_sp) {
+ /* if the function is running, cur_sp is not known so we
+ cannot mark the stack. Marking the variables is not needed
+ because a running function cannot be part of a removable
+ cycle */
+ for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
+ JS_MarkValue(rt, *sp, mark_func);
+ }
+}
+
+static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
+{
+ JSStackFrame *sf;
+ JSValue *sp;
+
+ sf = &s->frame;
+
+ /* close the closure variables. */
+ close_var_refs(rt, sf);
+
+ if (sf->arg_buf) {
+ /* cannot free the function if it is running */
+ assert(sf->cur_sp != NULL);
+ for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
+ JS_FreeValueRT(rt, *sp);
+ }
+ js_free_rt(rt, sf->arg_buf);
+ }
+ JS_FreeValueRT(rt, sf->cur_func);
+ JS_FreeValueRT(rt, s->this_val);
+}
+
+static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
+{
+ JSValue func_obj;
+
+ if (js_check_stack_overflow(ctx->rt, 0))
+ return JS_ThrowStackOverflow(ctx);
+
+ /* the tag does not matter provided it is not an object */
+ func_obj = JS_MKPTR(JS_TAG_INT, s);
+ return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
+ s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);
+}
+
+
+/* Generators */
+
+typedef enum JSGeneratorStateEnum {
+ JS_GENERATOR_STATE_SUSPENDED_START,
+ JS_GENERATOR_STATE_SUSPENDED_YIELD,
+ JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
+ JS_GENERATOR_STATE_EXECUTING,
+ JS_GENERATOR_STATE_COMPLETED,
+} JSGeneratorStateEnum;
+
+typedef struct JSGeneratorData {
+ JSGeneratorStateEnum state;
+ JSAsyncFunctionState func_state;
+} JSGeneratorData;
+
+static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
+{
+ if (s->state == JS_GENERATOR_STATE_COMPLETED)
+ return;
+ async_func_free(rt, &s->func_state);
+ s->state = JS_GENERATOR_STATE_COMPLETED;
+}
+
+static void js_generator_finalizer(JSRuntime *rt, JSValue obj)
+{
+ JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
+
+ if (s) {
+ free_generator_stack_rt(rt, s);
+ js_free_rt(rt, s);
+ }
+}
+
+static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
+{
+ free_generator_stack_rt(ctx->rt, s);
+}
+
+static void js_generator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSGeneratorData *s = p->u.generator_data;
+
+ if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
+ return;
+ async_func_mark(rt, &s->func_state, mark_func);
+}
+
+/* XXX: use enum */
+#define GEN_MAGIC_NEXT 0
+#define GEN_MAGIC_RETURN 1
+#define GEN_MAGIC_THROW 2
+
+static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
+ JSStackFrame *sf;
+ JSValue ret, func_ret;
+
+ *pdone = TRUE;
+ if (!s)
+ return JS_ThrowTypeError(ctx, "not a generator");
+ sf = &s->func_state.frame;
+ switch(s->state) {
+ default:
+ case JS_GENERATOR_STATE_SUSPENDED_START:
+ if (magic == GEN_MAGIC_NEXT) {
+ goto exec_no_arg;
+ } else {
+ free_generator_stack(ctx, s);
+ goto done;
+ }
+ break;
+ case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
+ case JS_GENERATOR_STATE_SUSPENDED_YIELD:
+ /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
+ ret = JS_DupValue(ctx, argv[0]);
+ if (magic == GEN_MAGIC_THROW &&
+ s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
+ JS_Throw(ctx, ret);
+ s->func_state.throw_flag = TRUE;
+ } else {
+ sf->cur_sp[-1] = ret;
+ sf->cur_sp[0] = JS_NewInt32(ctx, magic);
+ sf->cur_sp++;
+ exec_no_arg:
+ s->func_state.throw_flag = FALSE;
+ }
+ s->state = JS_GENERATOR_STATE_EXECUTING;
+ func_ret = async_func_resume(ctx, &s->func_state);
+ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
+ if (JS_IsException(func_ret)) {
+ /* finalize the execution in case of exception */
+ free_generator_stack(ctx, s);
+ return func_ret;
+ }
+ if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
+ /* get the returned yield value at the top of the stack */
+ ret = sf->cur_sp[-1];
+ sf->cur_sp[-1] = JS_UNDEFINED;
+ if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
+ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
+ /* return (value, done) object */
+ *pdone = 2;
+ } else {
+ *pdone = FALSE;
+ }
+ } else {
+ /* end of iterator */
+ ret = sf->cur_sp[-1];
+ sf->cur_sp[-1] = JS_UNDEFINED;
+ JS_FreeValue(ctx, func_ret);
+ free_generator_stack(ctx, s);
+ }
+ break;
+ case JS_GENERATOR_STATE_COMPLETED:
+ done:
+ /* execution is finished */
+ switch(magic) {
+ default:
+ case GEN_MAGIC_NEXT:
+ ret = JS_UNDEFINED;
+ break;
+ case GEN_MAGIC_RETURN:
+ ret = JS_DupValue(ctx, argv[0]);
+ break;
+ case GEN_MAGIC_THROW:
+ ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
+ break;
+ }
+ break;
+ case JS_GENERATOR_STATE_EXECUTING:
+ ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
+ break;
+ }
+ return ret;
+}
+
+static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int flags)
+{
+ JSValue obj, func_ret;
+ JSGeneratorData *s;
+
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ return JS_EXCEPTION;
+ s->state = JS_GENERATOR_STATE_SUSPENDED_START;
+ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
+ s->state = JS_GENERATOR_STATE_COMPLETED;
+ goto fail;
+ }
+
+ /* execute the function up to 'OP_initial_yield' */
+ func_ret = async_func_resume(ctx, &s->func_state);
+ if (JS_IsException(func_ret))
+ goto fail;
+ JS_FreeValue(ctx, func_ret);
+
+ obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);
+ if (JS_IsException(obj))
+ goto fail;
+ JS_SetOpaque(obj, s);
+ return obj;
+ fail:
+ free_generator_stack_rt(ctx->rt, s);
+ js_free(ctx, s);
+ return JS_EXCEPTION;
+}
+
+/* AsyncFunction */
+
+static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)
+{
+ if (s->is_active) {
+ async_func_free(rt, &s->func_state);
+ s->is_active = FALSE;
+ }
+}
+
+static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)
+{
+ js_async_function_terminate(rt, s);
+ JS_FreeValueRT(rt, s->resolving_funcs[0]);
+ JS_FreeValueRT(rt, s->resolving_funcs[1]);
+ remove_gc_object(&s->header);
+ js_free_rt(rt, s);
+}
+
+static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)
+{
+ if (--s->header.ref_count == 0) {
+ js_async_function_free0(rt, s);
+ }
+}
+
+static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSAsyncFunctionData *s = p->u.async_function_data;
+ if (s) {
+ js_async_function_free(rt, s);
+ }
+}
+
+static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSAsyncFunctionData *s = p->u.async_function_data;
+ if (s) {
+ mark_func(rt, &s->header);
+ }
+}
+
+static int js_async_function_resolve_create(JSContext *ctx,
+ JSAsyncFunctionData *s,
+ JSValue *resolving_funcs)
+{
+ int i;
+ JSObject *p;
+
+ for(i = 0; i < 2; i++) {
+ resolving_funcs[i] =
+ JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);
+ if (JS_IsException(resolving_funcs[i])) {
+ if (i == 1)
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ return -1;
+ }
+ p = JS_VALUE_GET_OBJ(resolving_funcs[i]);
+ s->header.ref_count++;
+ p->u.async_function_data = s;
+ }
+ return 0;
+}
+
+static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+{
+ JSValue func_ret, ret2;
+
+ func_ret = async_func_resume(ctx, &s->func_state);
+ if (JS_IsException(func_ret)) {
+ JSValue error;
+ fail:
+ error = JS_GetException(ctx);
+ ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
+ 1, &error);
+ JS_FreeValue(ctx, error);
+ js_async_function_terminate(ctx->rt, s);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
+ } else {
+ JSValue value;
+ value = s->func_state.frame.cur_sp[-1];
+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
+ if (JS_IsUndefined(func_ret)) {
+ /* function returned */
+ ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
+ 1, &value);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, value);
+ js_async_function_terminate(ctx->rt, s);
+ } else {
+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];
+ int i, res;
+
+ /* await */
+ JS_FreeValue(ctx, func_ret); /* not used */
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, &value, 0);
+ JS_FreeValue(ctx, value);
+ if (JS_IsException(promise))
+ goto fail;
+ if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
+ JS_FreeValue(ctx, promise);
+ goto fail;
+ }
+
+ /* Note: no need to create 'thrownawayCapability' as in
+ the spec */
+ for(i = 0; i < 2; i++)
+ resolving_funcs1[i] = JS_UNDEFINED;
+ res = perform_promise_then(ctx, promise,
+ (JSValueConst *)resolving_funcs,
+ (JSValueConst *)resolving_funcs1);
+ JS_FreeValue(ctx, promise);
+ for(i = 0; i < 2; i++)
+ JS_FreeValue(ctx, resolving_funcs[i]);
+ if (res)
+ goto fail;
+ }
+ }
+}
+
+static JSValue js_async_function_resolve_call(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int flags)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(func_obj);
+ JSAsyncFunctionData *s = p->u.async_function_data;
+ BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
+ JSValueConst arg;
+
+ if (argc > 0)
+ arg = argv[0];
+ else
+ arg = JS_UNDEFINED;
+ s->func_state.throw_flag = is_reject;
+ if (is_reject) {
+ JS_Throw(ctx, JS_DupValue(ctx, arg));
+ } else {
+ /* return value of await */
+ s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
+ }
+ js_async_function_resume(ctx, s);
+ return JS_UNDEFINED;
+}
+
+static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSValue promise;
+ JSAsyncFunctionData *s;
+
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ return JS_EXCEPTION;
+ s->header.ref_count = 1;
+ add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
+ s->is_active = FALSE;
+ s->resolving_funcs[0] = JS_UNDEFINED;
+ s->resolving_funcs[1] = JS_UNDEFINED;
+
+ promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
+ if (JS_IsException(promise))
+ goto fail;
+
+ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
+ fail:
+ JS_FreeValue(ctx, promise);
+ js_async_function_free(ctx->rt, s);
+ return JS_EXCEPTION;
+ }
+ s->is_active = TRUE;
+
+ js_async_function_resume(ctx, s);
+
+ js_async_function_free(ctx->rt, s);
+
+ return promise;
+}
+
+/* AsyncGenerator */
+
+typedef enum JSAsyncGeneratorStateEnum {
+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,
+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,
+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
+ JS_ASYNC_GENERATOR_STATE_EXECUTING,
+ JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,
+ JS_ASYNC_GENERATOR_STATE_COMPLETED,
+} JSAsyncGeneratorStateEnum;
+
+typedef struct JSAsyncGeneratorRequest {
+ struct list_head link;
+ /* completion */
+ int completion_type; /* GEN_MAGIC_x */
+ JSValue result;
+ /* promise capability */
+ JSValue promise;
+ JSValue resolving_funcs[2];
+} JSAsyncGeneratorRequest;
+
+typedef struct JSAsyncGeneratorData {
+ JSObject *generator; /* back pointer to the object (const) */
+ JSAsyncGeneratorStateEnum state;
+ JSAsyncFunctionState func_state;
+ struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
+} JSAsyncGeneratorData;
+
+static void js_async_generator_free(JSRuntime *rt,
+ JSAsyncGeneratorData *s)
+{
+ struct list_head *el, *el1;
+ JSAsyncGeneratorRequest *req;
+
+ list_for_each_safe(el, el1, &s->queue) {
+ req = list_entry(el, JSAsyncGeneratorRequest, link);
+ JS_FreeValueRT(rt, req->result);
+ JS_FreeValueRT(rt, req->promise);
+ JS_FreeValueRT(rt, req->resolving_funcs[0]);
+ JS_FreeValueRT(rt, req->resolving_funcs[1]);
+ js_free_rt(rt, req);
+ }
+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
+ s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
+ async_func_free(rt, &s->func_state);
+ }
+ js_free_rt(rt, s);
+}
+
+static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)
+{
+ JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);
+
+ if (s) {
+ js_async_generator_free(rt, s);
+ }
+}
+
+static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);
+ struct list_head *el;
+ JSAsyncGeneratorRequest *req;
+ if (s) {
+ list_for_each(el, &s->queue) {
+ req = list_entry(el, JSAsyncGeneratorRequest, link);
+ JS_MarkValue(rt, req->result, mark_func);
+ JS_MarkValue(rt, req->promise, mark_func);
+ JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
+ JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
+ }
+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
+ s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
+ async_func_mark(rt, &s->func_state, mark_func);
+ }
+ }
+}
+
+static JSValue js_async_generator_resolve_function(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data);
+
+static int js_async_generator_resolve_function_create(JSContext *ctx,
+ JSValueConst generator,
+ JSValue *resolving_funcs,
+ BOOL is_resume_next)
+{
+ int i;
+ JSValue func;
+
+ for(i = 0; i < 2; i++) {
+ func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
+ i + is_resume_next * 2, 1, &generator);
+ if (JS_IsException(func)) {
+ if (i == 1)
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ return -1;
+ }
+ resolving_funcs[i] = func;
+ }
+ return 0;
+}
+
+static int js_async_generator_await(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst value)
+{
+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];
+ int i, res;
+
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, &value, 0);
+ if (JS_IsException(promise))
+ goto fail;
+
+ if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
+ resolving_funcs, FALSE)) {
+ JS_FreeValue(ctx, promise);
+ goto fail;
+ }
+
+ /* Note: no need to create 'thrownawayCapability' as in
+ the spec */
+ for(i = 0; i < 2; i++)
+ resolving_funcs1[i] = JS_UNDEFINED;
+ res = perform_promise_then(ctx, promise,
+ (JSValueConst *)resolving_funcs,
+ (JSValueConst *)resolving_funcs1);
+ JS_FreeValue(ctx, promise);
+ for(i = 0; i < 2; i++)
+ JS_FreeValue(ctx, resolving_funcs[i]);
+ if (res)
+ goto fail;
+ return 0;
+ fail:
+ return -1;
+}
+
+static void js_async_generator_resolve_or_reject(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst result,
+ int is_reject)
+{
+ JSAsyncGeneratorRequest *next;
+ JSValue ret;
+
+ next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
+ list_del(&next->link);
+ ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
+ &result);
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, next->result);
+ JS_FreeValue(ctx, next->promise);
+ JS_FreeValue(ctx, next->resolving_funcs[0]);
+ JS_FreeValue(ctx, next->resolving_funcs[1]);
+ js_free(ctx, next);
+}
+
+static void js_async_generator_resolve(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst value,
+ BOOL done)
+{
+ JSValue result;
+ result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
+ /* XXX: better exception handling ? */
+ js_async_generator_resolve_or_reject(ctx, s, result, 0);
+ JS_FreeValue(ctx, result);
+ }
+
+static void js_async_generator_reject(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst exception)
+{
+ js_async_generator_resolve_or_reject(ctx, s, exception, 1);
+}
+
+static void js_async_generator_complete(JSContext *ctx,
+ JSAsyncGeneratorData *s)
+{
+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
+ async_func_free(ctx->rt, &s->func_state);
+ }
+}
+
+static int js_async_generator_completed_return(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst value)
+{
+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];
+ int res;
+
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, &value, 0);
+ if (JS_IsException(promise))
+ return -1;
+ if (js_async_generator_resolve_function_create(ctx,
+ JS_MKPTR(JS_TAG_OBJECT, s->generator),
+ resolving_funcs1,
+ TRUE)) {
+ JS_FreeValue(ctx, promise);
+ return -1;
+ }
+ resolving_funcs[0] = JS_UNDEFINED;
+ resolving_funcs[1] = JS_UNDEFINED;
+ res = perform_promise_then(ctx, promise,
+ (JSValueConst *)resolving_funcs1,
+ (JSValueConst *)resolving_funcs);
+ JS_FreeValue(ctx, resolving_funcs1[0]);
+ JS_FreeValue(ctx, resolving_funcs1[1]);
+ JS_FreeValue(ctx, promise);
+ return res;
+}
+
+static void js_async_generator_resume_next(JSContext *ctx,
+ JSAsyncGeneratorData *s)
+{
+ JSAsyncGeneratorRequest *next;
+ JSValue func_ret, value;
+
+ for(;;) {
+ if (list_empty(&s->queue))
+ break;
+ next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
+ switch(s->state) {
+ case JS_ASYNC_GENERATOR_STATE_EXECUTING:
+ /* only happens when restarting execution after await() */
+ goto resume_exec;
+ case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
+ goto done;
+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
+ if (next->completion_type == GEN_MAGIC_NEXT) {
+ goto exec_no_arg;
+ } else {
+ js_async_generator_complete(ctx, s);
+ }
+ break;
+ case JS_ASYNC_GENERATOR_STATE_COMPLETED:
+ if (next->completion_type == GEN_MAGIC_NEXT) {
+ js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
+ } else if (next->completion_type == GEN_MAGIC_RETURN) {
+ s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
+ js_async_generator_completed_return(ctx, s, next->result);
+ goto done;
+ } else {
+ js_async_generator_reject(ctx, s, next->result);
+ }
+ goto done;
+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
+ value = JS_DupValue(ctx, next->result);
+ if (next->completion_type == GEN_MAGIC_THROW &&
+ s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
+ JS_Throw(ctx, value);
+ s->func_state.throw_flag = TRUE;
+ } else {
+ /* 'yield' returns a value. 'yield *' also returns a value
+ in case the 'throw' method is called */
+ s->func_state.frame.cur_sp[-1] = value;
+ s->func_state.frame.cur_sp[0] =
+ JS_NewInt32(ctx, next->completion_type);
+ s->func_state.frame.cur_sp++;
+ exec_no_arg:
+ s->func_state.throw_flag = FALSE;
+ }
+ s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
+ resume_exec:
+ func_ret = async_func_resume(ctx, &s->func_state);
+ if (JS_IsException(func_ret)) {
+ value = JS_GetException(ctx);
+ js_async_generator_complete(ctx, s);
+ js_async_generator_reject(ctx, s, value);
+ JS_FreeValue(ctx, value);
+ } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
+ int func_ret_code;
+ value = s->func_state.frame.cur_sp[-1];
+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
+ func_ret_code = JS_VALUE_GET_INT(func_ret);
+ switch(func_ret_code) {
+ case FUNC_RET_YIELD:
+ case FUNC_RET_YIELD_STAR:
+ if (func_ret_code == FUNC_RET_YIELD_STAR)
+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
+ else
+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
+ js_async_generator_resolve(ctx, s, value, FALSE);
+ JS_FreeValue(ctx, value);
+ break;
+ case FUNC_RET_AWAIT:
+ js_async_generator_await(ctx, s, value);
+ JS_FreeValue(ctx, value);
+ goto done;
+ default:
+ abort();
+ }
+ } else {
+ assert(JS_IsUndefined(func_ret));
+ /* end of function */
+ value = s->func_state.frame.cur_sp[-1];
+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
+ js_async_generator_complete(ctx, s);
+ js_async_generator_resolve(ctx, s, value, TRUE);
+ JS_FreeValue(ctx, value);
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+ done: ;
+}
+
+static JSValue js_async_generator_resolve_function(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ BOOL is_reject = magic & 1;
+ JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
+ JSValueConst arg = argv[0];
+
+ /* XXX: what if s == NULL */
+
+ if (magic >= 2) {
+ /* resume next case in AWAITING_RETURN state */
+ assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
+ s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
+ if (is_reject) {
+ js_async_generator_reject(ctx, s, arg);
+ } else {
+ js_async_generator_resolve(ctx, s, arg, TRUE);
+ }
+ } else {
+ /* restart function execution after await() */
+ assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
+ s->func_state.throw_flag = is_reject;
+ if (is_reject) {
+ JS_Throw(ctx, JS_DupValue(ctx, arg));
+ } else {
+ /* return value of await */
+ s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
+ }
+ js_async_generator_resume_next(ctx, s);
+ }
+ return JS_UNDEFINED;
+}
+
+/* magic = GEN_MAGIC_x */
+static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic)
+{
+ JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);
+ JSValue promise, resolving_funcs[2];
+ JSAsyncGeneratorRequest *req;
+
+ promise = JS_NewPromiseCapability(ctx, resolving_funcs);
+ if (JS_IsException(promise))
+ return JS_EXCEPTION;
+ if (!s) {
+ JSValue err, res2;
+ JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
+ err = JS_GetException(ctx);
+ res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+ 1, &err);
+ JS_FreeValue(ctx, err);
+ JS_FreeValue(ctx, res2);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return promise;
+ }
+ req = js_mallocz(ctx, sizeof(*req));
+ if (!req)
+ goto fail;
+ req->completion_type = magic;
+ req->result = JS_DupValue(ctx, argv[0]);
+ req->promise = JS_DupValue(ctx, promise);
+ req->resolving_funcs[0] = resolving_funcs[0];
+ req->resolving_funcs[1] = resolving_funcs[1];
+ list_add_tail(&req->link, &s->queue);
+ if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {
+ js_async_generator_resume_next(ctx, s);
+ }
+ return promise;
+ fail:
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ JS_FreeValue(ctx, promise);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int flags)
+{
+ JSValue obj, func_ret;
+ JSAsyncGeneratorData *s;
+
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ return JS_EXCEPTION;
+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
+ init_list_head(&s->queue);
+ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
+ goto fail;
+ }
+
+ /* execute the function up to 'OP_initial_yield' (no yield nor
+ await are possible) */
+ func_ret = async_func_resume(ctx, &s->func_state);
+ if (JS_IsException(func_ret))
+ goto fail;
+ JS_FreeValue(ctx, func_ret);
+
+ obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);
+ if (JS_IsException(obj))
+ goto fail;
+ s->generator = JS_VALUE_GET_OBJ(obj);
+ JS_SetOpaque(obj, s);
+ return obj;
+ fail:
+ js_async_generator_free(ctx->rt, s);
+ return JS_EXCEPTION;
+}
+
+/* JS parser */
+
+enum {
+ TOK_NUMBER = -128,
+ TOK_STRING,
+ TOK_TEMPLATE,
+ TOK_IDENT,
+ TOK_REGEXP,
+ /* warning: order matters (see js_parse_assign_expr) */
+ TOK_MUL_ASSIGN,
+ TOK_DIV_ASSIGN,
+ TOK_MOD_ASSIGN,
+ TOK_PLUS_ASSIGN,
+ TOK_MINUS_ASSIGN,
+ TOK_SHL_ASSIGN,
+ TOK_SAR_ASSIGN,
+ TOK_SHR_ASSIGN,
+ TOK_AND_ASSIGN,
+ TOK_XOR_ASSIGN,
+ TOK_OR_ASSIGN,
+#ifdef CONFIG_BIGNUM
+ TOK_MATH_POW_ASSIGN,
+#endif
+ TOK_POW_ASSIGN,
+ TOK_LAND_ASSIGN,
+ TOK_LOR_ASSIGN,
+ TOK_DOUBLE_QUESTION_MARK_ASSIGN,
+ TOK_DEC,
+ TOK_INC,
+ TOK_SHL,
+ TOK_SAR,
+ TOK_SHR,
+ TOK_LT,
+ TOK_LTE,
+ TOK_GT,
+ TOK_GTE,
+ TOK_EQ,
+ TOK_STRICT_EQ,
+ TOK_NEQ,
+ TOK_STRICT_NEQ,
+ TOK_LAND,
+ TOK_LOR,
+#ifdef CONFIG_BIGNUM
+ TOK_MATH_POW,
+#endif
+ TOK_POW,
+ TOK_ARROW,
+ TOK_ELLIPSIS,
+ TOK_DOUBLE_QUESTION_MARK,
+ TOK_QUESTION_MARK_DOT,
+ TOK_ERROR,
+ TOK_PRIVATE_NAME,
+ TOK_EOF,
+ /* keywords: WARNING: same order as atoms */
+ TOK_NULL, /* must be first */
+ TOK_FALSE,
+ TOK_TRUE,
+ TOK_IF,
+ TOK_ELSE,
+ TOK_RETURN,
+ TOK_VAR,
+ TOK_THIS,
+ TOK_DELETE,
+ TOK_VOID,
+ TOK_TYPEOF,
+ TOK_NEW,
+ TOK_IN,
+ TOK_INSTANCEOF,
+ TOK_DO,
+ TOK_WHILE,
+ TOK_FOR,
+ TOK_BREAK,
+ TOK_CONTINUE,
+ TOK_SWITCH,
+ TOK_CASE,
+ TOK_DEFAULT,
+ TOK_THROW,
+ TOK_TRY,
+ TOK_CATCH,
+ TOK_FINALLY,
+ TOK_FUNCTION,
+ TOK_DEBUGGER,
+ TOK_WITH,
+ /* FutureReservedWord */
+ TOK_CLASS,
+ TOK_CONST,
+ TOK_ENUM,
+ TOK_EXPORT,
+ TOK_EXTENDS,
+ TOK_IMPORT,
+ TOK_SUPER,
+ /* FutureReservedWords when parsing strict mode code */
+ TOK_IMPLEMENTS,
+ TOK_INTERFACE,
+ TOK_LET,
+ TOK_PACKAGE,
+ TOK_PRIVATE,
+ TOK_PROTECTED,
+ TOK_PUBLIC,
+ TOK_STATIC,
+ TOK_YIELD,
+ TOK_AWAIT, /* must be last */
+ TOK_OF, /* only used for js_parse_skip_parens_token() */
+};
+
+#define TOK_FIRST_KEYWORD TOK_NULL
+#define TOK_LAST_KEYWORD TOK_AWAIT
+
+/* unicode code points */
+#define CP_NBSP 0x00a0
+#define CP_BOM 0xfeff
+
+#define CP_LS 0x2028
+#define CP_PS 0x2029
+
+typedef struct BlockEnv {
+ struct BlockEnv *prev;
+ JSAtom label_name; /* JS_ATOM_NULL if none */
+ int label_break; /* -1 if none */
+ int label_cont; /* -1 if none */
+ int drop_count; /* number of stack elements to drop */
+ int label_finally; /* -1 if none */
+ int scope_level;
+ int has_iterator;
+} BlockEnv;
+
+typedef struct JSGlobalVar {
+ int cpool_idx; /* if >= 0, index in the constant pool for hoisted
+ function defintion*/
+ uint8_t force_init : 1; /* force initialization to undefined */
+ uint8_t is_lexical : 1; /* global let/const definition */
+ uint8_t is_const : 1; /* const definition */
+ int scope_level; /* scope of definition */
+ JSAtom var_name; /* variable name */
+} JSGlobalVar;
+
+typedef struct RelocEntry {
+ struct RelocEntry *next;
+ uint32_t addr; /* address to patch */
+ int size; /* address size: 1, 2 or 4 bytes */
+} RelocEntry;
+
+typedef struct JumpSlot {
+ int op;
+ int size;
+ int pos;
+ int label;
+} JumpSlot;
+
+typedef struct LabelSlot {
+ int ref_count;
+ int pos; /* phase 1 address, -1 means not resolved yet */
+ int pos2; /* phase 2 address, -1 means not resolved yet */
+ int addr; /* phase 3 address, -1 means not resolved yet */
+ RelocEntry *first_reloc;
+} LabelSlot;
+
+typedef struct LineNumberSlot {
+ uint32_t pc;
+ int line_num;
+} LineNumberSlot;
+
+typedef enum JSParseFunctionEnum {
+ JS_PARSE_FUNC_STATEMENT,
+ JS_PARSE_FUNC_VAR,
+ JS_PARSE_FUNC_EXPR,
+ JS_PARSE_FUNC_ARROW,
+ JS_PARSE_FUNC_GETTER,
+ JS_PARSE_FUNC_SETTER,
+ JS_PARSE_FUNC_METHOD,
+ JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
+ JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
+} JSParseFunctionEnum;
+
+typedef enum JSParseExportEnum {
+ JS_PARSE_EXPORT_NONE,
+ JS_PARSE_EXPORT_NAMED,
+ JS_PARSE_EXPORT_DEFAULT,
+} JSParseExportEnum;
+
+typedef struct JSFunctionDef {
+ JSContext *ctx;
+ struct JSFunctionDef *parent;
+ int parent_cpool_idx; /* index in the constant pool of the parent
+ or -1 if none */
+ int parent_scope_level; /* scope level in parent at point of definition */
+ struct list_head child_list; /* list of JSFunctionDef.link */
+ struct list_head link;
+
+ BOOL is_eval; /* TRUE if eval code */
+ int eval_type; /* only valid if is_eval = TRUE */
+ BOOL is_global_var; /* TRUE if variables are not defined locally:
+ eval global, eval module or non strict eval */
+ BOOL is_func_expr; /* TRUE if function expression */
+ BOOL has_home_object; /* TRUE if the home object is available */
+ BOOL has_prototype; /* true if a prototype field is necessary */
+ BOOL has_simple_parameter_list;
+ BOOL has_parameter_expressions; /* if true, an argument scope is created */
+ BOOL has_use_strict; /* to reject directive in special cases */
+ BOOL has_eval_call; /* true if the function contains a call to eval() */
+ BOOL has_arguments_binding; /* true if the 'arguments' binding is
+ available in the function */
+ BOOL has_this_binding; /* true if the 'this' and new.target binding are
+ available in the function */
+ BOOL new_target_allowed; /* true if the 'new.target' does not
+ throw a syntax error */
+ BOOL super_call_allowed; /* true if super() is allowed */
+ BOOL super_allowed; /* true if super. or super[] is allowed */
+ BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */
+ BOOL is_derived_class_constructor;
+ BOOL in_function_body;
+ BOOL backtrace_barrier;
+ JSFunctionKindEnum func_kind : 8;
+ JSParseFunctionEnum func_type : 8;
+ uint8_t js_mode; /* bitmap of JS_MODE_x */
+ JSAtom func_name; /* JS_ATOM_NULL if no name */
+
+ JSVarDef *vars;
+ int var_size; /* allocated size for vars[] */
+ int var_count;
+ JSVarDef *args;
+ int arg_size; /* allocated size for args[] */
+ int arg_count; /* number of arguments */
+ int defined_arg_count;
+ int var_object_idx; /* -1 if none */
+ int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
+ int arguments_var_idx; /* -1 if none */
+ int arguments_arg_idx; /* argument variable definition in argument scope,
+ -1 if none */
+ int func_var_idx; /* variable containing the current function (-1
+ if none, only used if is_func_expr is true) */
+ int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */
+ int this_var_idx; /* variable containg the 'this' value, -1 if none */
+ int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */
+ int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */
+ int home_object_var_idx;
+ BOOL need_home_object;
+
+ int scope_level; /* index into fd->scopes if the current lexical scope */
+ int scope_first; /* index into vd->vars of first lexically scoped variable */
+ int scope_size; /* allocated size of fd->scopes array */
+ int scope_count; /* number of entries used in the fd->scopes array */
+ JSVarScope *scopes;
+ JSVarScope def_scope_array[4];
+ int body_scope; /* scope of the body of the function or eval */
+
+ int global_var_count;
+ int global_var_size;
+ JSGlobalVar *global_vars;
+
+ DynBuf byte_code;
+ int last_opcode_pos; /* -1 if no last opcode */
+ int last_opcode_line_num;
+ BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */
+
+ LabelSlot *label_slots;
+ int label_size; /* allocated size for label_slots[] */
+ int label_count;
+ BlockEnv *top_break; /* break/continue label stack */
+
+ /* constant pool (strings, functions, numbers) */
+ JSValue *cpool;
+ int cpool_count;
+ int cpool_size;
+
+ /* list of variables in the closure */
+ int closure_var_count;
+ int closure_var_size;
+ JSClosureVar *closure_var;
+
+ JumpSlot *jump_slots;
+ int jump_size;
+ int jump_count;
+
+ LineNumberSlot *line_number_slots;
+ int line_number_size;
+ int line_number_count;
+ int line_number_last;
+ int line_number_last_pc;
+
+ /* pc2line table */
+ JSAtom filename;
+ int line_num;
+ DynBuf pc2line;
+
+ char *source; /* raw source, utf-8 encoded */
+ int source_len;
+
+ JSModuleDef *module; /* != NULL when parsing a module */
+} JSFunctionDef;
+
+typedef struct JSToken {
+ int val;
+ int line_num; /* line number of token start */
+ const uint8_t *ptr;
+ union {
+ struct {
+ JSValue str;
+ int sep;
+ } str;
+ struct {
+ JSValue val;
+#ifdef CONFIG_BIGNUM
+ slimb_t exponent; /* may be != 0 only if val is a float */
+#endif
+ } num;
+ struct {
+ JSAtom atom;
+ BOOL has_escape;
+ BOOL is_reserved;
+ } ident;
+ struct {
+ JSValue body;
+ JSValue flags;
+ } regexp;
+ } u;
+} JSToken;
+
+typedef struct JSParseState {
+ JSContext *ctx;
+ int last_line_num; /* line number of last token */
+ int line_num; /* line number of current offset */
+ const char *filename;
+ JSToken token;
+ BOOL got_lf; /* true if got line feed before the current token */
+ const uint8_t *last_ptr;
+ const uint8_t *buf_ptr;
+ const uint8_t *buf_end;
+
+ /* current function code */
+ JSFunctionDef *cur_func;
+ BOOL is_module; /* parsing a module */
+ BOOL allow_html_comments;
+ BOOL ext_json; /* true if accepting JSON superset */
+} JSParseState;
+
+typedef struct JSOpCode {
+#ifdef DUMP_BYTECODE
+ const char *name;
+#endif
+ uint8_t size; /* in bytes */
+ /* the opcodes remove n_pop items from the top of the stack, then
+ pushes n_push items */
+ uint8_t n_pop;
+ uint8_t n_push;
+ uint8_t fmt;
+} JSOpCode;
+
+static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
+#define FMT(f)
+#ifdef DUMP_BYTECODE
+#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
+#else
+#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },
+#endif
+#include "quickjs-opcode.h"
+#undef DEF
+#undef FMT
+};
+
+#if SHORT_OPCODES
+/* After the final compilation pass, short opcodes are used. Their
+ opcodes overlap with the temporary opcodes which cannot appear in
+ the final bytecode. Their description is after the temporary
+ opcodes in opcode_info[]. */
+#define short_opcode_info(op) \
+ opcode_info[(op) >= OP_TEMP_START ? \
+ (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]
+#else
+#define short_opcode_info(op) opcode_info[op]
+#endif
+
+static warn_unused int next_token(JSParseState *s);
+
+static void free_token(JSParseState *s, JSToken *token)
+{
+ switch(token->val) {
+#ifdef CONFIG_BIGNUM
+ case TOK_NUMBER:
+ JS_FreeValue(s->ctx, token->u.num.val);
+ break;
+#endif
+ case TOK_STRING:
+ case TOK_TEMPLATE:
+ JS_FreeValue(s->ctx, token->u.str.str);
+ break;
+ case TOK_REGEXP:
+ JS_FreeValue(s->ctx, token->u.regexp.body);
+ JS_FreeValue(s->ctx, token->u.regexp.flags);
+ break;
+ case TOK_IDENT:
+ case TOK_PRIVATE_NAME:
+ JS_FreeAtom(s->ctx, token->u.ident.atom);
+ break;
+ default:
+ if (token->val >= TOK_FIRST_KEYWORD &&
+ token->val <= TOK_LAST_KEYWORD) {
+ JS_FreeAtom(s->ctx, token->u.ident.atom);
+ }
+ break;
+ }
+}
+
+static void maybe_unused dump_token(JSParseState *s,
+ const JSToken *token)
+{
+ switch(token->val) {
+ case TOK_NUMBER:
+ {
+ double d;
+ JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */
+ printf("number: %.14g\n", d);
+ }
+ break;
+ case TOK_IDENT:
+ dump_atom:
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ printf("ident: '%s'\n",
+ JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));
+ }
+ break;
+ case TOK_STRING:
+ {
+ const char *str;
+ /* XXX: quote the string */
+ str = JS_ToCString(s->ctx, token->u.str.str);
+ printf("string: '%s'\n", str);
+ JS_FreeCString(s->ctx, str);
+ }
+ break;
+ case TOK_TEMPLATE:
+ {
+ const char *str;
+ str = JS_ToCString(s->ctx, token->u.str.str);
+ printf("template: `%s`\n", str);
+ JS_FreeCString(s->ctx, str);
+ }
+ break;
+ case TOK_REGEXP:
+ {
+ const char *str, *str2;
+ str = JS_ToCString(s->ctx, token->u.regexp.body);
+ str2 = JS_ToCString(s->ctx, token->u.regexp.flags);
+ printf("regexp: '%s' '%s'\n", str, str2);
+ JS_FreeCString(s->ctx, str);
+ JS_FreeCString(s->ctx, str2);
+ }
+ break;
+ case TOK_EOF:
+ printf("eof\n");
+ break;
+ default:
+ if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {
+ goto dump_atom;
+ } else if (s->token.val >= 256) {
+ printf("token: %d\n", token->val);
+ } else {
+ printf("token: '%c'\n", token->val);
+ }
+ break;
+ }
+}
+
+int FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, const char *fmt, ...)
+{
+ JSContext *ctx = s->ctx;
+ va_list ap;
+ int backtrace_flags;
+
+ va_start(ap, fmt);
+ JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
+ va_end(ap);
+ backtrace_flags = 0;
+ if (s->cur_func && s->cur_func->backtrace_barrier)
+ backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
+ build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,
+ backtrace_flags);
+ return -1;
+}
+
+static int js_parse_expect(JSParseState *s, int tok)
+{
+ if (s->token.val != tok) {
+ /* XXX: dump token correctly in all cases */
+ return js_parse_error(s, "expecting '%c'", tok);
+ }
+ return next_token(s);
+}
+
+static int js_parse_expect_semi(JSParseState *s)
+{
+ if (s->token.val != ';') {
+ /* automatic insertion of ';' */
+ if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {
+ return 0;
+ }
+ return js_parse_error(s, "expecting '%c'", ';');
+ }
+ return next_token(s);
+}
+
+static int js_parse_error_reserved_identifier(JSParseState *s)
+{
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ return js_parse_error(s, "'%s' is a reserved identifier",
+ JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),
+ s->token.u.ident.atom));
+}
+
+static warn_unused int js_parse_template_part(JSParseState *s, const uint8_t *p)
+{
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+
+ /* p points to the first byte of the template part */
+ if (string_buffer_init(s->ctx, b, 32))
+ goto fail;
+ for(;;) {
+ if (p >= s->buf_end)
+ goto unexpected_eof;
+ c = *p++;
+ if (c == '`') {
+ /* template end part */
+ break;
+ }
+ if (c == '$' && *p == '{') {
+ /* template start or middle part */
+ p++;
+ break;
+ }
+ if (c == '\\') {
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ if (p >= s->buf_end)
+ goto unexpected_eof;
+ c = *p++;
+ }
+ /* newline sequences are normalized as single '\n' bytes */
+ if (c == '\r') {
+ if (*p == '\n')
+ p++;
+ c = '\n';
+ }
+ if (c == '\n') {
+ s->line_num++;
+ } else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ js_parse_error(s, "invalid UTF-8 sequence");
+ goto fail;
+ }
+ p = p_next;
+ }
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ s->token.val = TOK_TEMPLATE;
+ s->token.u.str.sep = c;
+ s->token.u.str.str = string_buffer_end(b);
+ s->buf_ptr = p;
+ return 0;
+
+ unexpected_eof:
+ js_parse_error(s, "unexpected end of string");
+ fail:
+ string_buffer_free(b);
+ return -1;
+}
+
+static warn_unused int js_parse_string(JSParseState *s, int sep,
+ BOOL do_throw, const uint8_t *p,
+ JSToken *token, const uint8_t **pp)
+{
+ int ret;
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+
+ /* string */
+ if (string_buffer_init(s->ctx, b, 32))
+ goto fail;
+ for(;;) {
+ if (p >= s->buf_end)
+ goto invalid_char;
+ c = *p;
+ if (c < 0x20) {
+ if (!s->cur_func) {
+ if (do_throw)
+ js_parse_error(s, "invalid character in a JSON string");
+ goto fail;
+ }
+ if (sep == '`') {
+ if (c == '\r') {
+ if (p[1] == '\n')
+ p++;
+ c = '\n';
+ }
+ /* do not update s->line_num */
+ } else if (c == '\n' || c == '\r')
+ goto invalid_char;
+ }
+ p++;
+ if (c == sep)
+ break;
+ if (c == '$' && *p == '{' && sep == '`') {
+ /* template start or middle part */
+ p++;
+ break;
+ }
+ if (c == '\\') {
+ c = *p;
+ /* XXX: need a specific JSON case to avoid
+ accepting invalid escapes */
+ switch(c) {
+ case '\0':
+ if (p >= s->buf_end)
+ goto invalid_char;
+ p++;
+ break;
+ case '\'':
+ case '\"':
+ case '\\':
+ p++;
+ break;
+ case '\r': /* accept DOS and MAC newline sequences */
+ if (p[1] == '\n') {
+ p++;
+ }
+ /* fall thru */
+ case '\n':
+ /* ignore escaped newline sequence */
+ p++;
+ if (sep != '`')
+ s->line_num++;
+ continue;
+ default:
+ if (c >= '0' && c <= '9') {
+ if (!s->cur_func)
+ goto invalid_escape; /* JSON case */
+ if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
+ goto parse_escape;
+ if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
+ p++;
+ c = '\0';
+ } else {
+ if (c >= '8' || sep == '`') {
+ /* Note: according to ES2021, \8 and \9 are not
+ accepted in strict mode or in templates. */
+ goto invalid_escape;
+ } else {
+ if (do_throw)
+ js_parse_error(s, "octal escape sequences are not allowed in strict mode");
+ }
+ goto fail;
+ }
+ } else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ goto invalid_utf8;
+ }
+ p = p_next;
+ /* LS or PS are skipped */
+ if (c == CP_LS || c == CP_PS)
+ continue;
+ } else {
+ parse_escape:
+ ret = lre_parse_escape(&p, TRUE);
+ if (ret == -1) {
+ invalid_escape:
+ if (do_throw)
+ js_parse_error(s, "malformed escape sequence in string literal");
+ goto fail;
+ } else if (ret < 0) {
+ /* ignore the '\' (could output a warning) */
+ p++;
+ } else {
+ c = ret;
+ }
+ }
+ break;
+ }
+ } else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF)
+ goto invalid_utf8;
+ p = p_next;
+ }
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ token->val = TOK_STRING;
+ token->u.str.sep = c;
+ token->u.str.str = string_buffer_end(b);
+ *pp = p;
+ return 0;
+
+ invalid_utf8:
+ if (do_throw)
+ js_parse_error(s, "invalid UTF-8 sequence");
+ goto fail;
+ invalid_char:
+ if (do_throw)
+ js_parse_error(s, "unexpected end of string");
+ fail:
+ string_buffer_free(b);
+ return -1;
+}
+
+static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
+ return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&
+ !s->token.u.ident.has_escape;
+}
+
+static warn_unused int js_parse_regexp(JSParseState *s)
+{
+ const uint8_t *p;
+ BOOL in_class;
+ StringBuffer b_s, *b = &b_s;
+ StringBuffer b2_s, *b2 = &b2_s;
+ uint32_t c;
+
+ p = s->buf_ptr;
+ p++;
+ in_class = FALSE;
+ if (string_buffer_init(s->ctx, b, 32))
+ return -1;
+ if (string_buffer_init(s->ctx, b2, 1))
+ goto fail;
+ for(;;) {
+ if (p >= s->buf_end) {
+ eof_error:
+ js_parse_error(s, "unexpected end of regexp");
+ goto fail;
+ }
+ c = *p++;
+ if (c == '\n' || c == '\r') {
+ goto eol_error;
+ } else if (c == '/') {
+ if (!in_class)
+ break;
+ } else if (c == '[') {
+ in_class = TRUE;
+ } else if (c == ']') {
+ /* XXX: incorrect as the first character in a class */
+ in_class = FALSE;
+ } else if (c == '\\') {
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ c = *p++;
+ if (c == '\n' || c == '\r')
+ goto eol_error;
+ else if (c == '\0' && p >= s->buf_end)
+ goto eof_error;
+ else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ goto invalid_utf8;
+ }
+ p = p_next;
+ if (c == CP_LS || c == CP_PS)
+ goto eol_error;
+ }
+ } else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ invalid_utf8:
+ js_parse_error(s, "invalid UTF-8 sequence");
+ goto fail;
+ }
+ p = p_next;
+ /* LS or PS are considered as line terminator */
+ if (c == CP_LS || c == CP_PS) {
+ eol_error:
+ js_parse_error(s, "unexpected line terminator in regexp");
+ goto fail;
+ }
+ }
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+
+ /* flags */
+ for(;;) {
+ const uint8_t *p_next = p;
+ c = *p_next++;
+ if (c >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ goto invalid_utf8;
+ }
+ }
+ if (!lre_js_is_ident_next(c))
+ break;
+ if (string_buffer_putc(b2, c))
+ goto fail;
+ p = p_next;
+ }
+
+ s->token.val = TOK_REGEXP;
+ s->token.u.regexp.body = string_buffer_end(b);
+ s->token.u.regexp.flags = string_buffer_end(b2);
+ s->buf_ptr = p;
+ return 0;
+ fail:
+ string_buffer_free(b);
+ string_buffer_free(b2);
+ return -1;
+}
+
+static warn_unused int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
+ const char *static_buf)
+{
+ char *buf, *new_buf;
+ size_t size, new_size;
+
+ buf = *pbuf;
+ size = *psize;
+ if (size >= (SIZE_MAX / 3) * 2)
+ new_size = SIZE_MAX;
+ else
+ new_size = size + (size >> 1);
+ if (buf == static_buf) {
+ new_buf = js_malloc(ctx, new_size);
+ if (!new_buf)
+ return -1;
+ memcpy(new_buf, buf, size);
+ } else {
+ new_buf = js_realloc(ctx, buf, new_size);
+ if (!new_buf)
+ return -1;
+ }
+ *pbuf = new_buf;
+ *psize = new_size;
+ return 0;
+}
+
+/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
+static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
+ BOOL *pident_has_escape, int c, BOOL is_private)
+{
+ const uint8_t *p, *p1;
+ char ident_buf[128], *buf;
+ size_t ident_size, ident_pos;
+ JSAtom atom;
+
+ p = *pp;
+ buf = ident_buf;
+ ident_size = sizeof(ident_buf);
+ ident_pos = 0;
+ if (is_private)
+ buf[ident_pos++] = '#';
+ for(;;) {
+ p1 = p;
+
+ if (c < 128) {
+ buf[ident_pos++] = c;
+ } else {
+ ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);
+ }
+ c = *p1++;
+ if (c == '\\' && *p1 == 'u') {
+ c = lre_parse_escape(&p1, TRUE);
+ *pident_has_escape = TRUE;
+ } else if (c >= 128) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
+ }
+ if (!lre_js_is_ident_next(c))
+ break;
+ p = p1;
+ if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
+ if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
+ atom = JS_ATOM_NULL;
+ goto done;
+ }
+ }
+ }
+ atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
+ done:
+ if (unlikely(buf != ident_buf))
+ js_free(s->ctx, buf);
+ *pp = p;
+ return atom;
+}
+
+
+static warn_unused int next_token(JSParseState *s)
+{
+ const uint8_t *p;
+ int c;
+ BOOL ident_has_escape;
+ JSAtom atom;
+
+ if (js_check_stack_overflow(s->ctx->rt, 0)) {
+ return js_parse_error(s, "stack overflow");
+ }
+
+ free_token(s, &s->token);
+
+ p = s->last_ptr = s->buf_ptr;
+ s->got_lf = FALSE;
+ s->last_line_num = s->token.line_num;
+ redo:
+ s->token.line_num = s->line_num;
+ s->token.ptr = p;
+ c = *p;
+ switch(c) {
+ case 0:
+ if (p >= s->buf_end) {
+ s->token.val = TOK_EOF;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '`':
+ if (js_parse_template_part(s, p + 1))
+ goto fail;
+ p = s->buf_ptr;
+ break;
+ case '\'':
+ case '\"':
+ if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
+ goto fail;
+ break;
+ case '\r': /* accept DOS and MAC newline sequences */
+ if (p[1] == '\n') {
+ p++;
+ }
+ /* fall thru */
+ case '\n':
+ p++;
+ line_terminator:
+ s->got_lf = TRUE;
+ s->line_num++;
+ goto redo;
+ case '\f':
+ case '\v':
+ case ' ':
+ case '\t':
+ p++;
+ goto redo;
+ case '/':
+ if (p[1] == '*') {
+ /* comment */
+ p += 2;
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end) {
+ js_parse_error(s, "unexpected end of comment");
+ goto fail;
+ }
+ if (p[0] == '*' && p[1] == '/') {
+ p += 2;
+ break;
+ }
+ if (*p == '\n') {
+ s->line_num++;
+ s->got_lf = TRUE; /* considered as LF for ASI */
+ p++;
+ } else if (*p == '\r') {
+ s->got_lf = TRUE; /* considered as LF for ASI */
+ p++;
+ } else if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if (c == CP_LS || c == CP_PS) {
+ s->got_lf = TRUE; /* considered as LF for ASI */
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else if (p[1] == '/') {
+ /* line comment */
+ p += 2;
+ skip_line_comment:
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end)
+ break;
+ if (*p == '\r' || *p == '\n')
+ break;
+ if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ /* LS or PS are considered as line terminator */
+ if (c == CP_LS || c == CP_PS) {
+ break;
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_DIV_ASSIGN;
+ } else {
+ p++;
+ s->token.val = c;
+ }
+ break;
+ case '\\':
+ if (p[1] == 'u') {
+ const uint8_t *p1 = p + 1;
+ int c1 = lre_parse_escape(&p1, TRUE);
+ if (c1 >= 0 && lre_js_is_ident_first(c1)) {
+ c = c1;
+ p = p1;
+ ident_has_escape = TRUE;
+ goto has_ident;
+ } else {
+ /* XXX: syntax error? */
+ }
+ }
+ goto def_token;
+ case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f': case 'g': case 'h':
+ case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p':
+ case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D':
+ case 'E': case 'F': case 'G': case 'H':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+ case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case '$':
+ /* identifier */
+ p++;
+ ident_has_escape = FALSE;
+ has_ident:
+ atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ s->token.u.ident.atom = atom;
+ s->token.u.ident.has_escape = ident_has_escape;
+ s->token.u.ident.is_reserved = FALSE;
+ if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
+ (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
+ (s->cur_func->js_mode & JS_MODE_STRICT)) ||
+ (s->token.u.ident.atom == JS_ATOM_yield &&
+ ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
+ (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
+ !s->cur_func->in_function_body && s->cur_func->parent &&
+ (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
+ (s->token.u.ident.atom == JS_ATOM_await &&
+ (s->is_module ||
+ (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||
+ (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
+ !s->cur_func->in_function_body && s->cur_func->parent &&
+ (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {
+ if (ident_has_escape) {
+ s->token.u.ident.is_reserved = TRUE;
+ s->token.val = TOK_IDENT;
+ } else {
+ /* The keywords atoms are pre allocated */
+ s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
+ }
+ } else {
+ s->token.val = TOK_IDENT;
+ }
+ break;
+ case '#':
+ /* private name */
+ {
+ const uint8_t *p1;
+ p++;
+ p1 = p;
+ c = *p1++;
+ if (c == '\\' && *p1 == 'u') {
+ c = lre_parse_escape(&p1, TRUE);
+ } else if (c >= 128) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
+ }
+ if (!lre_js_is_ident_first(c)) {
+ js_parse_error(s, "invalid first character of private name");
+ goto fail;
+ }
+ p = p1;
+ ident_has_escape = FALSE; /* not used */
+ atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ s->token.u.ident.atom = atom;
+ s->token.val = TOK_PRIVATE_NAME;
+ }
+ break;
+ case '.':
+ if (p[1] == '.' && p[2] == '.') {
+ p += 3;
+ s->token.val = TOK_ELLIPSIS;
+ break;
+ }
+ if (p[1] >= '0' && p[1] <= '9') {
+ goto parse_number;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '0':
+ /* in strict mode, octal literals are not accepted */
+ if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
+ js_parse_error(s, "octal literals are deprecated in strict mode");
+ goto fail;
+ }
+ goto parse_number;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
+ /* number */
+ parse_number:
+ {
+ JSValue ret;
+ const uint8_t *p1;
+ int flags, radix;
+ flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
+ ATOD_ACCEPT_UNDERSCORES;
+#ifdef CONFIG_BIGNUM
+ flags |= ATOD_ACCEPT_SUFFIX;
+ if (s->cur_func->js_mode & JS_MODE_MATH) {
+ flags |= ATOD_MODE_BIGINT;
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ flags |= ATOD_TYPE_BIG_FLOAT;
+ }
+#endif
+ radix = 0;
+#ifdef CONFIG_BIGNUM
+ s->token.u.num.exponent = 0;
+ ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
+ flags, &s->token.u.num.exponent);
+#else
+ ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
+ flags);
+#endif
+ if (JS_IsException(ret))
+ goto fail;
+ /* reject `10instanceof Number` */
+ if (JS_VALUE_IS_NAN(ret) ||
+ lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {
+ JS_FreeValue(s->ctx, ret);
+ js_parse_error(s, "invalid number literal");
+ goto fail;
+ }
+ s->token.val = TOK_NUMBER;
+ s->token.u.num.val = ret;
+ }
+ break;
+ case '*':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_MUL_ASSIGN;
+ } else if (p[1] == '*') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_POW_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_POW;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+ case '%':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_MOD_ASSIGN;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '+':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_PLUS_ASSIGN;
+ } else if (p[1] == '+') {
+ p += 2;
+ s->token.val = TOK_INC;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '-':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_MINUS_ASSIGN;
+ } else if (p[1] == '-') {
+ if (s->allow_html_comments &&
+ p[2] == '>' && s->last_line_num != s->line_num) {
+ /* Annex B: `-->` at beginning of line is an html comment end.
+ It extends to the end of the line.
+ */
+ goto skip_line_comment;
+ }
+ p += 2;
+ s->token.val = TOK_DEC;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '<':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_LTE;
+ } else if (p[1] == '<') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_SHL_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_SHL;
+ }
+ } else if (s->allow_html_comments &&
+ p[1] == '!' && p[2] == '-' && p[3] == '-') {
+ /* Annex B: handle `<!--` single line html comments */
+ goto skip_line_comment;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '>':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_GTE;
+ } else if (p[1] == '>') {
+ if (p[2] == '>') {
+ if (p[3] == '=') {
+ p += 4;
+ s->token.val = TOK_SHR_ASSIGN;
+ } else {
+ p += 3;
+ s->token.val = TOK_SHR;
+ }
+ } else if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_SAR_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_SAR;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+ case '=':
+ if (p[1] == '=') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_STRICT_EQ;
+ } else {
+ p += 2;
+ s->token.val = TOK_EQ;
+ }
+ } else if (p[1] == '>') {
+ p += 2;
+ s->token.val = TOK_ARROW;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '!':
+ if (p[1] == '=') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_STRICT_NEQ;
+ } else {
+ p += 2;
+ s->token.val = TOK_NEQ;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+ case '&':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_AND_ASSIGN;
+ } else if (p[1] == '&') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_LAND_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_LAND;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ /* in math mode, '^' is the power operator. '^^' is always the
+ xor operator and '**' is always the power operator */
+ case '^':
+ if (p[1] == '=') {
+ p += 2;
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ s->token.val = TOK_MATH_POW_ASSIGN;
+ else
+ s->token.val = TOK_XOR_ASSIGN;
+ } else if (p[1] == '^') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_XOR_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = '^';
+ }
+ } else {
+ p++;
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ s->token.val = TOK_MATH_POW;
+ else
+ s->token.val = '^';
+ }
+ break;
+#else
+ case '^':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_XOR_ASSIGN;
+ } else {
+ goto def_token;
+ }
+ break;
+#endif
+ case '|':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_OR_ASSIGN;
+ } else if (p[1] == '|') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_LOR_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_LOR;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+ case '?':
+ if (p[1] == '?') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_DOUBLE_QUESTION_MARK;
+ }
+ } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
+ p += 2;
+ s->token.val = TOK_QUESTION_MARK_DOT;
+ } else {
+ goto def_token;
+ }
+ break;
+ default:
+ if (c >= 128) {
+ /* unicode value */
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ switch(c) {
+ case CP_PS:
+ case CP_LS:
+ /* XXX: should avoid incrementing line_number, but
+ needed to handle HTML comments */
+ goto line_terminator;
+ default:
+ if (lre_is_space(c)) {
+ goto redo;
+ } else if (lre_js_is_ident_first(c)) {
+ ident_has_escape = FALSE;
+ goto has_ident;
+ } else {
+ js_parse_error(s, "unexpected character");
+ goto fail;
+ }
+ }
+ }
+ def_token:
+ s->token.val = c;
+ p++;
+ break;
+ }
+ s->buf_ptr = p;
+
+ // dump_token(s, &s->token);
+ return 0;
+
+ fail:
+ s->token.val = TOK_ERROR;
+ return -1;
+}
+
+/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
+static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
+{
+ const uint8_t *p;
+ char ident_buf[128], *buf;
+ size_t ident_size, ident_pos;
+ JSAtom atom;
+
+ p = *pp;
+ buf = ident_buf;
+ ident_size = sizeof(ident_buf);
+ ident_pos = 0;
+ for(;;) {
+ buf[ident_pos++] = c;
+ c = *p;
+ if (c >= 128 ||
+ !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))
+ break;
+ p++;
+ if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
+ if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
+ atom = JS_ATOM_NULL;
+ goto done;
+ }
+ }
+ }
+ atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
+ done:
+ if (unlikely(buf != ident_buf))
+ js_free(s->ctx, buf);
+ *pp = p;
+ return atom;
+}
+
+static warn_unused int json_next_token(JSParseState *s)
+{
+ const uint8_t *p;
+ int c;
+ JSAtom atom;
+
+ if (js_check_stack_overflow(s->ctx->rt, 0)) {
+ return js_parse_error(s, "stack overflow");
+ }
+
+ free_token(s, &s->token);
+
+ p = s->last_ptr = s->buf_ptr;
+ s->last_line_num = s->token.line_num;
+ redo:
+ s->token.line_num = s->line_num;
+ s->token.ptr = p;
+ c = *p;
+ switch(c) {
+ case 0:
+ if (p >= s->buf_end) {
+ s->token.val = TOK_EOF;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '\'':
+ if (!s->ext_json) {
+ /* JSON does not accept single quoted strings */
+ goto def_token;
+ }
+ /* fall through */
+ case '\"':
+ if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
+ goto fail;
+ break;
+ case '\r': /* accept DOS and MAC newline sequences */
+ if (p[1] == '\n') {
+ p++;
+ }
+ /* fall thru */
+ case '\n':
+ p++;
+ s->line_num++;
+ goto redo;
+ case '\f':
+ case '\v':
+ if (!s->ext_json) {
+ /* JSONWhitespace does not match <VT>, nor <FF> */
+ goto def_token;
+ }
+ /* fall through */
+ case ' ':
+ case '\t':
+ p++;
+ goto redo;
+ case '/':
+ if (!s->ext_json) {
+ /* JSON does not accept comments */
+ goto def_token;
+ }
+ if (p[1] == '*') {
+ /* comment */
+ p += 2;
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end) {
+ js_parse_error(s, "unexpected end of comment");
+ goto fail;
+ }
+ if (p[0] == '*' && p[1] == '/') {
+ p += 2;
+ break;
+ }
+ if (*p == '\n') {
+ s->line_num++;
+ p++;
+ } else if (*p == '\r') {
+ p++;
+ } else if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else if (p[1] == '/') {
+ /* line comment */
+ p += 2;
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end)
+ break;
+ if (*p == '\r' || *p == '\n')
+ break;
+ if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ /* LS or PS are considered as line terminator */
+ if (c == CP_LS || c == CP_PS) {
+ break;
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else {
+ goto def_token;
+ }
+ break;
+ case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f': case 'g': case 'h':
+ case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p':
+ case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D':
+ case 'E': case 'F': case 'G': case 'H':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+ case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case '$':
+ /* identifier : only pure ascii characters are accepted */
+ p++;
+ atom = json_parse_ident(s, &p, c);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ s->token.u.ident.atom = atom;
+ s->token.u.ident.has_escape = FALSE;
+ s->token.u.ident.is_reserved = FALSE;
+ s->token.val = TOK_IDENT;
+ break;
+ case '+':
+ if (!s->ext_json || !is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '0':
+ if (is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '-':
+ if (!is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
+ /* number */
+ parse_number:
+ {
+ JSValue ret;
+ int flags, radix;
+ if (!s->ext_json) {
+ flags = 0;
+ radix = 10;
+ } else {
+ flags = ATOD_ACCEPT_BIN_OCT;
+ radix = 0;
+ }
+ ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
+ flags);
+ if (JS_IsException(ret))
+ goto fail;
+ s->token.val = TOK_NUMBER;
+ s->token.u.num.val = ret;
+ }
+ break;
+ default:
+ if (c >= 128) {
+ js_parse_error(s, "unexpected character");
+ goto fail;
+ }
+ def_token:
+ s->token.val = c;
+ p++;
+ break;
+ }
+ s->buf_ptr = p;
+
+ // dump_token(s, &s->token);
+ return 0;
+
+ fail:
+ s->token.val = TOK_ERROR;
+ return -1;
+}
+
+/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is
+ only set if TOK_IMPORT is returned */
+/* XXX: handle all unicode cases */
+static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
+{
+ const uint8_t *p;
+ uint32_t c;
+
+ /* skip spaces and comments */
+ p = *pp;
+ for (;;) {
+ switch(c = *p++) {
+ case '\r':
+ case '\n':
+ if (no_line_terminator)
+ return '\n';
+ continue;
+ case ' ':
+ case '\t':
+ case '\v':
+ case '\f':
+ continue;
+ case '/':
+ if (*p == '/') {
+ if (no_line_terminator)
+ return '\n';
+ while (*p && *p != '\r' && *p != '\n')
+ p++;
+ continue;
+ }
+ if (*p == '*') {
+ while (*++p) {
+ if ((*p == '\r' || *p == '\n') && no_line_terminator)
+ return '\n';
+ if (*p == '*' && p[1] == '/') {
+ p += 2;
+ break;
+ }
+ }
+ continue;
+ }
+ break;
+ case '=':
+ if (*p == '>')
+ return TOK_ARROW;
+ break;
+ default:
+ if (lre_js_is_ident_first(c)) {
+ if (c == 'i') {
+ if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) {
+ return TOK_IN;
+ }
+ if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' &&
+ p[3] == 'r' && p[4] == 't' &&
+ !lre_js_is_ident_next(p[5])) {
+ *pp = p + 5;
+ return TOK_IMPORT;
+ }
+ } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {
+ return TOK_OF;
+ } else if (c == 'e' &&
+ p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&
+ p[3] == 'r' && p[4] == 't' &&
+ !lre_js_is_ident_next(p[5])) {
+ *pp = p + 5;
+ return TOK_EXPORT;
+ } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&
+ p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&
+ p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {
+ return TOK_FUNCTION;
+ }
+ return TOK_IDENT;
+ }
+ break;
+ }
+ return c;
+ }
+}
+
+static int peek_token(JSParseState *s, BOOL no_line_terminator)
+{
+ const uint8_t *p = s->buf_ptr;
+ return simple_next_token(&p, no_line_terminator);
+}
+
+/* return true if 'input' contains the source of a module
+ (heuristic). 'input' must be a zero terminated.
+
+ Heuristic: skip comments and expect 'import' keyword not followed
+ by '(' or '.' or export keyword.
+*/
+BOOL JS_DetectModule(const char *input, size_t input_len)
+{
+ const uint8_t *p = (const uint8_t *)input;
+ int tok;
+ switch(simple_next_token(&p, FALSE)) {
+ case TOK_IMPORT:
+ tok = simple_next_token(&p, FALSE);
+ return (tok != '.' && tok != '(');
+ case TOK_EXPORT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static inline int get_prev_opcode(JSFunctionDef *fd) {
+ if (fd->last_opcode_pos < 0)
+ return OP_invalid;
+ else
+ return fd->byte_code.buf[fd->last_opcode_pos];
+}
+
+static BOOL js_is_live_code(JSParseState *s) {
+ switch (get_prev_opcode(s->cur_func)) {
+ case OP_tail_call:
+ case OP_tail_call_method:
+ case OP_return:
+ case OP_return_undef:
+ case OP_return_async:
+ case OP_throw:
+ case OP_throw_error:
+ case OP_goto:
+#if SHORT_OPCODES
+ case OP_goto8:
+ case OP_goto16:
+#endif
+ case OP_ret:
+ return FALSE;
+ default:
+ return TRUE;
+ }
+}
+
+static void emit_u8(JSParseState *s, uint8_t val)
+{
+ dbuf_putc(&s->cur_func->byte_code, val);
+}
+
+static void emit_u16(JSParseState *s, uint16_t val)
+{
+ dbuf_put_u16(&s->cur_func->byte_code, val);
+}
+
+static void emit_u32(JSParseState *s, uint32_t val)
+{
+ dbuf_put_u32(&s->cur_func->byte_code, val);
+}
+
+static void emit_op(JSParseState *s, uint8_t val)
+{
+ JSFunctionDef *fd = s->cur_func;
+ DynBuf *bc = &fd->byte_code;
+
+ /* Use the line number of the last token used, not the next token,
+ nor the current offset in the source file.
+ */
+ if (unlikely(fd->last_opcode_line_num != s->last_line_num)) {
+ dbuf_putc(bc, OP_line_num);
+ dbuf_put_u32(bc, s->last_line_num);
+ fd->last_opcode_line_num = s->last_line_num;
+ }
+ fd->last_opcode_pos = bc->size;
+ dbuf_putc(bc, val);
+}
+
+static void emit_atom(JSParseState *s, JSAtom name)
+{
+ emit_u32(s, JS_DupAtom(s->ctx, name));
+}
+
+static int update_label(JSFunctionDef *s, int label, int delta)
+{
+ LabelSlot *ls;
+
+ assert(label >= 0 && label < s->label_count);
+ ls = &s->label_slots[label];
+ ls->ref_count += delta;
+ assert(ls->ref_count >= 0);
+ return ls->ref_count;
+}
+
+static int new_label_fd(JSFunctionDef *fd, int label)
+{
+ LabelSlot *ls;
+
+ if (label < 0) {
+ if (js_resize_array(fd->ctx, (void *)&fd->label_slots,
+ sizeof(fd->label_slots[0]),
+ &fd->label_size, fd->label_count + 1))
+ return -1;
+ label = fd->label_count++;
+ ls = &fd->label_slots[label];
+ ls->ref_count = 0;
+ ls->pos = -1;
+ ls->pos2 = -1;
+ ls->addr = -1;
+ ls->first_reloc = NULL;
+ }
+ return label;
+}
+
+static int new_label(JSParseState *s)
+{
+ return new_label_fd(s->cur_func, -1);
+}
+
+/* return the label ID offset */
+static int emit_label(JSParseState *s, int label)
+{
+ if (label >= 0) {
+ emit_op(s, OP_label);
+ emit_u32(s, label);
+ s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
+ return s->cur_func->byte_code.size - 4;
+ } else {
+ return -1;
+ }
+}
+
+/* return label or -1 if dead code */
+static int emit_goto(JSParseState *s, int opcode, int label)
+{
+ if (js_is_live_code(s)) {
+ if (label < 0)
+ label = new_label(s);
+ emit_op(s, opcode);
+ emit_u32(s, label);
+ s->cur_func->label_slots[label].ref_count++;
+ return label;
+ }
+ return -1;
+}
+
+/* return the constant pool index. 'val' is not duplicated. */
+static int cpool_add(JSParseState *s, JSValue val)
+{
+ JSFunctionDef *fd = s->cur_func;
+
+ if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
+ &fd->cpool_size, fd->cpool_count + 1))
+ return -1;
+ fd->cpool[fd->cpool_count++] = val;
+ return fd->cpool_count - 1;
+}
+
+static warn_unused int emit_push_const(JSParseState *s, JSValueConst val,
+ BOOL as_atom)
+{
+ int idx;
+
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {
+ JSAtom atom;
+ /* warning: JS_NewAtomStr frees the string value */
+ JS_DupValue(s->ctx, val);
+ atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
+ if (atom != JS_ATOM_NULL && !JS_AtomIsTaggedInt(atom)) {
+ emit_op(s, OP_push_atom_value);
+ emit_u32(s, atom);
+ return 0;
+ }
+ }
+
+ idx = cpool_add(s, JS_DupValue(s->ctx, val));
+ if (idx < 0)
+ return -1;
+ emit_op(s, OP_push_const);
+ emit_u32(s, idx);
+ return 0;
+}
+
+/* return the variable index or -1 if not found,
+ add ARGUMENT_VAR_OFFSET for argument variables */
+static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ int i;
+ for(i = fd->arg_count; i-- > 0;) {
+ if (fd->args[i].var_name == name)
+ return i | ARGUMENT_VAR_OFFSET;
+ }
+ return -1;
+}
+
+static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ int i;
+ for(i = fd->var_count; i-- > 0;) {
+ if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
+ return i;
+ }
+ return find_arg(ctx, fd, name);
+}
+
+/* find a variable declaration in a given scope */
+static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd,
+ JSAtom name, int scope_level)
+{
+ int scope_idx;
+ for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0;
+ scope_idx = fd->vars[scope_idx].scope_next) {
+ if (fd->vars[scope_idx].scope_level != scope_level)
+ break;
+ if (fd->vars[scope_idx].var_name == name)
+ return scope_idx;
+ }
+ return -1;
+}
+
+/* return true if scope == parent_scope or if scope is a child of
+ parent_scope */
+static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,
+ int scope, int parent_scope)
+{
+ while (scope >= 0) {
+ if (scope == parent_scope)
+ return TRUE;
+ scope = fd->scopes[scope].parent;
+ }
+ return FALSE;
+}
+
+/* find a 'var' declaration in the same scope or a child scope */
+static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,
+ JSAtom name, int scope_level)
+{
+ int i;
+ for(i = 0; i < fd->var_count; i++) {
+ JSVarDef *vd = &fd->vars[i];
+ if (vd->var_name == name && vd->scope_level == 0) {
+ if (is_child_scope(ctx, fd, vd->scope_next,
+ scope_level))
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name)
+{
+ int i;
+ for(i = 0; i < fd->global_var_count; i++) {
+ JSGlobalVar *hf = &fd->global_vars[i];
+ if (hf->var_name == name)
+ return hf;
+ }
+ return NULL;
+
+}
+
+static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name)
+{
+ JSGlobalVar *hf = find_global_var(fd, name);
+ if (hf && hf->is_lexical)
+ return hf;
+ else
+ return NULL;
+}
+
+static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
+ int scope_idx, BOOL check_catch_var)
+{
+ while (scope_idx >= 0) {
+ JSVarDef *vd = &fd->vars[scope_idx];
+ if (vd->var_name == name &&
+ (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&
+ check_catch_var)))
+ return scope_idx;
+ scope_idx = vd->scope_next;
+ }
+
+ if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {
+ if (find_lexical_global_var(fd, name))
+ return GLOBAL_VAR_OFFSET;
+ }
+ return -1;
+}
+
+static int push_scope(JSParseState *s) {
+ if (s->cur_func) {
+ JSFunctionDef *fd = s->cur_func;
+ int scope = fd->scope_count;
+ /* XXX: should check for scope overflow */
+ if ((fd->scope_count + 1) > fd->scope_size) {
+ int new_size;
+ size_t slack;
+ JSVarScope *new_buf;
+ /* XXX: potential arithmetic overflow */
+ new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);
+ if (fd->scopes == fd->def_scope_array) {
+ new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);
+ if (!new_buf)
+ return -1;
+ memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));
+ } else {
+ new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);
+ if (!new_buf)
+ return -1;
+ }
+ new_size += slack / sizeof(*new_buf);
+ fd->scopes = new_buf;
+ fd->scope_size = new_size;
+ }
+ fd->scope_count++;
+ fd->scopes[scope].parent = fd->scope_level;
+ fd->scopes[scope].first = fd->scope_first;
+ emit_op(s, OP_enter_scope);
+ emit_u16(s, scope);
+ return fd->scope_level = scope;
+ }
+ return 0;
+}
+
+static int get_first_lexical_var(JSFunctionDef *fd, int scope)
+{
+ while (scope >= 0) {
+ int scope_idx = fd->scopes[scope].first;
+ if (scope_idx >= 0)
+ return scope_idx;
+ scope = fd->scopes[scope].parent;
+ }
+ return -1;
+}
+
+static void pop_scope(JSParseState *s) {
+ if (s->cur_func) {
+ /* disable scoped variables */
+ JSFunctionDef *fd = s->cur_func;
+ int scope = fd->scope_level;
+ emit_op(s, OP_leave_scope);
+ emit_u16(s, scope);
+ fd->scope_level = fd->scopes[scope].parent;
+ fd->scope_first = get_first_lexical_var(fd, fd->scope_level);
+ }
+}
+
+static void close_scopes(JSParseState *s, int scope, int scope_stop)
+{
+ while (scope > scope_stop) {
+ emit_op(s, OP_leave_scope);
+ emit_u16(s, scope);
+ scope = s->cur_func->scopes[scope].parent;
+ }
+}
+
+/* return the variable index or -1 if error */
+static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ JSVarDef *vd;
+
+ /* the local variable indexes are currently stored on 16 bits */
+ if (fd->var_count >= JS_MAX_LOCAL_VARS) {
+ JS_ThrowInternalError(ctx, "too many local variables");
+ return -1;
+ }
+ if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),
+ &fd->var_size, fd->var_count + 1))
+ return -1;
+ vd = &fd->vars[fd->var_count++];
+ memset(vd, 0, sizeof(*vd));
+ vd->var_name = JS_DupAtom(ctx, name);
+ vd->func_pool_idx = -1;
+ return fd->var_count - 1;
+}
+
+static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
+ JSVarKindEnum var_kind)
+{
+ int idx = add_var(ctx, fd, name);
+ if (idx >= 0) {
+ JSVarDef *vd = &fd->vars[idx];
+ vd->var_kind = var_kind;
+ vd->scope_level = fd->scope_level;
+ vd->scope_next = fd->scope_first;
+ fd->scopes[fd->scope_level].first = idx;
+ fd->scope_first = idx;
+ }
+ return idx;
+}
+
+static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ int idx = fd->func_var_idx;
+ if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {
+ fd->func_var_idx = idx;
+ fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME;
+ if (fd->js_mode & JS_MODE_STRICT)
+ fd->vars[idx].is_const = TRUE;
+ }
+ return idx;
+}
+
+static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd)
+{
+ int idx = fd->arguments_var_idx;
+ if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) {
+ fd->arguments_var_idx = idx;
+ }
+ return idx;
+}
+
+/* add an argument definition in the argument scope. Only needed when
+ "eval()" may be called in the argument scope. Return 0 if OK. */
+static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd)
+{
+ int idx;
+ if (fd->arguments_arg_idx < 0) {
+ idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX);
+ if (idx < 0) {
+ /* XXX: the scope links are not fully updated. May be an
+ issue if there are child scopes of the argument
+ scope */
+ idx = add_var(ctx, fd, JS_ATOM_arguments);
+ if (idx < 0)
+ return -1;
+ fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first;
+ fd->scopes[ARG_SCOPE_INDEX].first = idx;
+ fd->vars[idx].scope_level = ARG_SCOPE_INDEX;
+ fd->vars[idx].is_lexical = TRUE;
+
+ fd->arguments_arg_idx = idx;
+ }
+ }
+ return 0;
+}
+
+static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ JSVarDef *vd;
+
+ /* the local variable indexes are currently stored on 16 bits */
+ if (fd->arg_count >= JS_MAX_LOCAL_VARS) {
+ JS_ThrowInternalError(ctx, "too many arguments");
+ return -1;
+ }
+ if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),
+ &fd->arg_size, fd->arg_count + 1))
+ return -1;
+ vd = &fd->args[fd->arg_count++];
+ memset(vd, 0, sizeof(*vd));
+ vd->var_name = JS_DupAtom(ctx, name);
+ vd->func_pool_idx = -1;
+ return fd->arg_count - 1;
+}
+
+/* add a global variable definition */
+static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s,
+ JSAtom name)
+{
+ JSGlobalVar *hf;
+
+ if (js_resize_array(ctx, (void **)&s->global_vars,
+ sizeof(s->global_vars[0]),
+ &s->global_var_size, s->global_var_count + 1))
+ return NULL;
+ hf = &s->global_vars[s->global_var_count++];
+ hf->cpool_idx = -1;
+ hf->force_init = FALSE;
+ hf->is_lexical = FALSE;
+ hf->is_const = FALSE;
+ hf->scope_level = s->scope_level;
+ hf->var_name = JS_DupAtom(ctx, name);
+ return hf;
+}
+
+typedef enum {
+ JS_VAR_DEF_WITH,
+ JS_VAR_DEF_LET,
+ JS_VAR_DEF_CONST,
+ JS_VAR_DEF_FUNCTION_DECL, /* function declaration */
+ JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
+ JS_VAR_DEF_CATCH,
+ JS_VAR_DEF_VAR,
+} JSVarDefEnum;
+
+static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
+ JSVarDefEnum var_def_type)
+{
+ JSContext *ctx = s->ctx;
+ JSVarDef *vd;
+ int idx;
+
+ switch (var_def_type) {
+ case JS_VAR_DEF_WITH:
+ idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);
+ break;
+
+ case JS_VAR_DEF_LET:
+ case JS_VAR_DEF_CONST:
+ case JS_VAR_DEF_FUNCTION_DECL:
+ case JS_VAR_DEF_NEW_FUNCTION_DECL:
+ idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);
+ if (idx >= 0) {
+ if (idx < GLOBAL_VAR_OFFSET) {
+ if (fd->vars[idx].scope_level == fd->scope_level) {
+ /* same scope: in non strict mode, functions
+ can be redefined (annex B.3.3.4). */
+ if (!(!(fd->js_mode & JS_MODE_STRICT) &&
+ var_def_type == JS_VAR_DEF_FUNCTION_DECL &&
+ fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {
+ goto redef_lex_error;
+ }
+ } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {
+ goto redef_lex_error;
+ }
+ } else {
+ if (fd->scope_level == fd->body_scope) {
+ redef_lex_error:
+ /* redefining a scoped var in the same scope: error */
+ return js_parse_error(s, "invalid redefinition of lexical identifier");
+ }
+ }
+ }
+ if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&
+ var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&
+ fd->scope_level == fd->body_scope &&
+ find_arg(ctx, fd, name) >= 0) {
+ /* lexical variable redefines a parameter name */
+ return js_parse_error(s, "invalid redefinition of parameter name");
+ }
+
+ if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {
+ return js_parse_error(s, "invalid redefinition of a variable");
+ }
+
+ if (fd->is_global_var) {
+ JSGlobalVar *hf;
+ hf = find_global_var(fd, name);
+ if (hf && is_child_scope(ctx, fd, hf->scope_level,
+ fd->scope_level)) {
+ return js_parse_error(s, "invalid redefinition of global identifier");
+ }
+ }
+
+ if (fd->is_eval &&
+ (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
+ fd->eval_type == JS_EVAL_TYPE_MODULE) &&
+ fd->scope_level == fd->body_scope) {
+ JSGlobalVar *hf;
+ hf = add_global_var(s->ctx, fd, name);
+ if (!hf)
+ return -1;
+ hf->is_lexical = TRUE;
+ hf->is_const = (var_def_type == JS_VAR_DEF_CONST);
+ idx = GLOBAL_VAR_OFFSET;
+ } else {
+ JSVarKindEnum var_kind;
+ if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)
+ var_kind = JS_VAR_FUNCTION_DECL;
+ else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)
+ var_kind = JS_VAR_NEW_FUNCTION_DECL;
+ else
+ var_kind = JS_VAR_NORMAL;
+ idx = add_scope_var(ctx, fd, name, var_kind);
+ if (idx >= 0) {
+ vd = &fd->vars[idx];
+ vd->is_lexical = 1;
+ vd->is_const = (var_def_type == JS_VAR_DEF_CONST);
+ }
+ }
+ break;
+
+ case JS_VAR_DEF_CATCH:
+ idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);
+ break;
+
+ case JS_VAR_DEF_VAR:
+ if (find_lexical_decl(ctx, fd, name, fd->scope_first,
+ FALSE) >= 0) {
+ invalid_lexical_redefinition:
+ /* error to redefine a var that inside a lexical scope */
+ return js_parse_error(s, "invalid redefinition of lexical identifier");
+ }
+ if (fd->is_global_var) {
+ JSGlobalVar *hf;
+ hf = find_global_var(fd, name);
+ if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&
+ fd->eval_type == JS_EVAL_TYPE_MODULE) {
+ goto invalid_lexical_redefinition;
+ }
+ hf = add_global_var(s->ctx, fd, name);
+ if (!hf)
+ return -1;
+ idx = GLOBAL_VAR_OFFSET;
+ } else {
+ /* if the variable already exists, don't add it again */
+ idx = find_var(ctx, fd, name);
+ if (idx >= 0)
+ break;
+ idx = add_var(ctx, fd, name);
+ if (idx >= 0) {
+ if (name == JS_ATOM_arguments && fd->has_arguments_binding)
+ fd->arguments_var_idx = idx;
+ fd->vars[idx].scope_next = fd->scope_level;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ return idx;
+}
+
+/* add a private field variable in the current scope */
+static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
+ JSAtom name, JSVarKindEnum var_kind)
+{
+ JSContext *ctx = s->ctx;
+ JSVarDef *vd;
+ int idx;
+
+ idx = add_scope_var(ctx, fd, name, var_kind);
+ if (idx < 0)
+ return idx;
+ vd = &fd->vars[idx];
+ vd->is_lexical = 1;
+ vd->is_const = 1;
+ return idx;
+}
+
+static warn_unused int js_parse_expr(JSParseState *s);
+static warn_unused int js_parse_function_decl(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name, const uint8_t *ptr,
+ int start_line);
+static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
+static warn_unused int js_parse_function_decl2(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+ const uint8_t *ptr,
+ int function_line_num,
+ JSParseExportEnum export_flag,
+ JSFunctionDef **pfd);
+static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags);
+static warn_unused int js_parse_assign_expr(JSParseState *s);
+static warn_unused int js_parse_unary(JSParseState *s, int parse_flags);
+static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
+ JSAtom label_name,
+ int label_break, int label_cont,
+ int drop_count);
+static void pop_break_entry(JSFunctionDef *fd);
+static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
+ JSAtom local_name, JSAtom export_name,
+ JSExportTypeEnum export_type);
+
+/* Note: all the fields are already sealed except length */
+static int seal_template_obj(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property1(p, JS_ATOM_length);
+ if (prs) {
+ if (js_update_property_flags(ctx, p, &prs,
+ prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))
+ return -1;
+ }
+ p->extensible = FALSE;
+ return 0;
+}
+
+static warn_unused int js_parse_template(JSParseState *s, int call, int *argc)
+{
+ JSContext *ctx = s->ctx;
+ JSValue raw_array, template_object;
+ JSToken cooked;
+ int depth, ret;
+
+ raw_array = JS_UNDEFINED; /* avoid warning */
+ template_object = JS_UNDEFINED; /* avoid warning */
+ if (call) {
+ /* Create a template object: an array of cooked strings */
+ /* Create an array of raw strings and store it to the raw property */
+ template_object = JS_NewArray(ctx);
+ if (JS_IsException(template_object))
+ return -1;
+ // pool_idx = s->cur_func->cpool_count;
+ ret = emit_push_const(s, template_object, 0);
+ JS_FreeValue(ctx, template_object);
+ if (ret)
+ return -1;
+ raw_array = JS_NewArray(ctx);
+ if (JS_IsException(raw_array))
+ return -1;
+ if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,
+ raw_array, JS_PROP_THROW) < 0) {
+ return -1;
+ }
+ }
+
+ depth = 0;
+ while (s->token.val == TOK_TEMPLATE) {
+ const uint8_t *p = s->token.ptr + 1;
+ cooked = s->token;
+ if (call) {
+ if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,
+ JS_DupValue(ctx, s->token.u.str.str),
+ JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
+ return -1;
+ }
+ /* re-parse the string with escape sequences but do not throw a
+ syntax error if it contains invalid sequences
+ */
+ if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {
+ cooked.u.str.str = JS_UNDEFINED;
+ }
+ if (JS_DefinePropertyValueUint32(ctx, template_object, depth,
+ cooked.u.str.str,
+ JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
+ return -1;
+ }
+ } else {
+ JSString *str;
+ /* re-parse the string with escape sequences and throw a
+ syntax error if it contains invalid sequences
+ */
+ JS_FreeValue(ctx, s->token.u.str.str);
+ s->token.u.str.str = JS_UNDEFINED;
+ if (js_parse_string(s, '`', TRUE, p, &cooked, &p))
+ return -1;
+ str = JS_VALUE_GET_STRING(cooked.u.str.str);
+ if (str->len != 0 || depth == 0) {
+ ret = emit_push_const(s, cooked.u.str.str, 1);
+ JS_FreeValue(s->ctx, cooked.u.str.str);
+ if (ret)
+ return -1;
+ if (depth == 0) {
+ if (s->token.u.str.sep == '`')
+ goto done1;
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_concat);
+ }
+ depth++;
+ } else {
+ JS_FreeValue(s->ctx, cooked.u.str.str);
+ }
+ }
+ if (s->token.u.str.sep == '`')
+ goto done;
+ if (next_token(s))
+ return -1;
+ if (js_parse_expr(s))
+ return -1;
+ depth++;
+ if (s->token.val != '}') {
+ return js_parse_error(s, "expected '}' after template expression");
+ }
+ /* XXX: should convert to string at this stage? */
+ free_token(s, &s->token);
+ /* Resume TOK_TEMPLATE parsing (s->token.line_num and
+ * s->token.ptr are OK) */
+ s->got_lf = FALSE;
+ s->last_line_num = s->token.line_num;
+ if (js_parse_template_part(s, s->buf_ptr))
+ return -1;
+ }
+ return js_parse_expect(s, TOK_TEMPLATE);
+
+ done:
+ if (call) {
+ /* Seal the objects */
+ seal_template_obj(ctx, raw_array);
+ seal_template_obj(ctx, template_object);
+ *argc = depth + 1;
+ } else {
+ emit_op(s, OP_call_method);
+ emit_u16(s, depth - 1);
+ }
+ done1:
+ return next_token(s);
+}
+
+
+#define PROP_TYPE_IDENT 0
+#define PROP_TYPE_VAR 1
+#define PROP_TYPE_GET 2
+#define PROP_TYPE_SET 3
+#define PROP_TYPE_STAR 4
+#define PROP_TYPE_ASYNC 5
+#define PROP_TYPE_ASYNC_STAR 6
+
+#define PROP_TYPE_PRIVATE (1 << 4)
+
+static BOOL token_is_ident(int tok)
+{
+ /* Accept keywords and reserved words as property names */
+ return (tok == TOK_IDENT ||
+ (tok >= TOK_FIRST_KEYWORD &&
+ tok <= TOK_LAST_KEYWORD));
+}
+
+/* if the property is an expression, name = JS_ATOM_NULL */
+static int warn_unused js_parse_property_name(JSParseState *s,
+ JSAtom *pname,
+ BOOL allow_method, BOOL allow_var,
+ BOOL allow_private)
+{
+ int is_private = 0;
+ BOOL is_non_reserved_ident;
+ JSAtom name;
+ int prop_type;
+
+ prop_type = PROP_TYPE_IDENT;
+ if (allow_method) {
+ if (token_is_pseudo_keyword(s, JS_ATOM_get)
+ || token_is_pseudo_keyword(s, JS_ATOM_set)) {
+ /* get x(), set x() */
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ if (s->token.val == ':' || s->token.val == ',' ||
+ s->token.val == '}' || s->token.val == '(') {
+ is_non_reserved_ident = TRUE;
+ goto ident_found;
+ }
+ prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);
+ JS_FreeAtom(s->ctx, name);
+ } else if (s->token.val == '*') {
+ if (next_token(s))
+ goto fail;
+ prop_type = PROP_TYPE_STAR;
+ } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) != '\n') {
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ if (s->token.val == ':' || s->token.val == ',' ||
+ s->token.val == '}' || s->token.val == '(') {
+ is_non_reserved_ident = TRUE;
+ goto ident_found;
+ }
+ JS_FreeAtom(s->ctx, name);
+ if (s->token.val == '*') {
+ if (next_token(s))
+ goto fail;
+ prop_type = PROP_TYPE_ASYNC_STAR;
+ } else {
+ prop_type = PROP_TYPE_ASYNC;
+ }
+ }
+ }
+
+ if (token_is_ident(s->token.val)) {
+ /* variable can only be a non-reserved identifier */
+ is_non_reserved_ident =
+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);
+ /* keywords and reserved words have a valid atom */
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ ident_found:
+ if (is_non_reserved_ident &&
+ prop_type == PROP_TYPE_IDENT && allow_var) {
+ if (!(s->token.val == ':' ||
+ (s->token.val == '(' && allow_method))) {
+ prop_type = PROP_TYPE_VAR;
+ }
+ }
+ } else if (s->token.val == TOK_STRING) {
+ name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
+ if (name == JS_ATOM_NULL)
+ goto fail;
+ if (next_token(s))
+ goto fail1;
+ } else if (s->token.val == TOK_NUMBER) {
+ JSValue val;
+ val = s->token.u.num.val;
+#ifdef CONFIG_BIGNUM
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ val = s->ctx->rt->bigfloat_ops.
+ mul_pow10_to_float64(s->ctx, &p->num,
+ s->token.u.num.exponent);
+ if (JS_IsException(val))
+ goto fail;
+ name = JS_ValueToAtom(s->ctx, val);
+ JS_FreeValue(s->ctx, val);
+ } else
+#endif
+ {
+ name = JS_ValueToAtom(s->ctx, val);
+ }
+ if (name == JS_ATOM_NULL)
+ goto fail;
+ if (next_token(s))
+ goto fail1;
+ } else if (s->token.val == '[') {
+ if (next_token(s))
+ goto fail;
+ if (js_parse_expr(s))
+ goto fail;
+ if (js_parse_expect(s, ']'))
+ goto fail;
+ name = JS_ATOM_NULL;
+ } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ is_private = PROP_TYPE_PRIVATE;
+ } else {
+ goto invalid_prop;
+ }
+ if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&
+ s->token.val != '(') {
+ JS_FreeAtom(s->ctx, name);
+ invalid_prop:
+ js_parse_error(s, "invalid property name");
+ goto fail;
+ }
+ *pname = name;
+ return prop_type | is_private;
+ fail1:
+ JS_FreeAtom(s->ctx, name);
+ fail:
+ *pname = JS_ATOM_NULL;
+ return -1;
+}
+
+typedef struct JSParsePos {
+ int last_line_num;
+ int line_num;
+ BOOL got_lf;
+ const uint8_t *ptr;
+} JSParsePos;
+
+static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
+{
+ sp->last_line_num = s->last_line_num;
+ sp->line_num = s->token.line_num;
+ sp->ptr = s->token.ptr;
+ sp->got_lf = s->got_lf;
+ return 0;
+}
+
+static warn_unused int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
+{
+ s->token.line_num = sp->last_line_num;
+ s->line_num = sp->line_num;
+ s->buf_ptr = sp->ptr;
+ s->got_lf = sp->got_lf;
+ return next_token(s);
+}
+
+/* return TRUE if a regexp literal is allowed after this token */
+static BOOL is_regexp_allowed(int tok)
+{
+ switch (tok) {
+ case TOK_NUMBER:
+ case TOK_STRING:
+ case TOK_REGEXP:
+ case TOK_DEC:
+ case TOK_INC:
+ case TOK_NULL:
+ case TOK_FALSE:
+ case TOK_TRUE:
+ case TOK_THIS:
+ case ')':
+ case ']':
+ case '}': /* XXX: regexp may occur after */
+ case TOK_IDENT:
+ return FALSE;
+ default:
+ return TRUE;
+ }
+}
+
+#define SKIP_HAS_SEMI (1 << 0)
+#define SKIP_HAS_ELLIPSIS (1 << 1)
+#define SKIP_HAS_ASSIGNMENT (1 << 2)
+
+/* XXX: improve speed with early bailout */
+/* XXX: no longer works if regexps are present. Could use previous
+ regexp parsing heuristics to handle most cases */
+static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)
+{
+ char state[256];
+ size_t level = 0;
+ JSParsePos pos;
+ int last_tok, tok = TOK_EOF;
+ int tok_len, bits = 0;
+ char c;
+
+ /* protect from underflow */
+ state[level++] = 0;
+
+ js_parse_get_pos(s, &pos);
+ last_tok = 0;
+ for (;;) {
+ switch(s->token.val) {
+ case '(':
+ case '[':
+ case '{':
+ if (level >= sizeof(state))
+ goto done;
+ state[level++] = s->token.val;
+ break;
+ case ')':
+ if (state[--level] != '(')
+ goto done;
+ break;
+ case ']':
+ if (state[--level] != '[')
+ goto done;
+ break;
+ case '}':
+ c = state[--level];
+ if (c == '`') {
+ /* continue the parsing of the template */
+ free_token(s, &s->token);
+ /* Resume TOK_TEMPLATE parsing (s->token.line_num and
+ * s->token.ptr are OK) */
+ s->got_lf = FALSE;
+ s->last_line_num = s->token.line_num;
+ if (js_parse_template_part(s, s->buf_ptr))
+ goto done;
+ goto handle_template;
+ } else if (c != '{') {
+ goto done;
+ }
+ break;
+ case TOK_TEMPLATE:
+ handle_template:
+ if (s->token.u.str.sep != '`') {
+ /* '${' inside the template : closing '}' and continue
+ parsing the template */
+ if (level >= sizeof(state))
+ goto done;
+ state[level++] = '`';
+ }
+ break;
+ case TOK_EOF:
+ goto done;
+ case ';':
+ if (level == 2) {
+ bits |= SKIP_HAS_SEMI;
+ }
+ break;
+ case TOK_ELLIPSIS:
+ if (level == 2) {
+ bits |= SKIP_HAS_ELLIPSIS;
+ }
+ break;
+ case '=':
+ bits |= SKIP_HAS_ASSIGNMENT;
+ break;
+
+ case TOK_DIV_ASSIGN:
+ tok_len = 2;
+ goto parse_regexp;
+ case '/':
+ tok_len = 1;
+ parse_regexp:
+ if (is_regexp_allowed(last_tok)) {
+ s->buf_ptr -= tok_len;
+ if (js_parse_regexp(s)) {
+ /* XXX: should clear the exception */
+ goto done;
+ }
+ }
+ break;
+ }
+ /* last_tok is only used to recognize regexps */
+ if (s->token.val == TOK_IDENT &&
+ (token_is_pseudo_keyword(s, JS_ATOM_of) ||
+ token_is_pseudo_keyword(s, JS_ATOM_yield))) {
+ last_tok = TOK_OF;
+ } else {
+ last_tok = s->token.val;
+ }
+ if (next_token(s)) {
+ /* XXX: should clear the exception generated by next_token() */
+ break;
+ }
+ if (level <= 1) {
+ tok = s->token.val;
+ if (token_is_pseudo_keyword(s, JS_ATOM_of))
+ tok = TOK_OF;
+ if (no_line_terminator && s->last_line_num != s->token.line_num)
+ tok = '\n';
+ break;
+ }
+ }
+ done:
+ if (pbits) {
+ *pbits = bits;
+ }
+ if (js_parse_seek_token(s, &pos))
+ return -1;
+ return tok;
+}
+
+static void set_object_name(JSParseState *s, JSAtom name)
+{
+ JSFunctionDef *fd = s->cur_func;
+ int opcode;
+
+ opcode = get_prev_opcode(fd);
+ if (opcode == OP_set_name) {
+ /* XXX: should free atom after OP_set_name? */
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+ emit_op(s, OP_set_name);
+ emit_atom(s, name);
+ } else if (opcode == OP_set_class_name) {
+ int define_class_pos;
+ JSAtom atom;
+ define_class_pos = fd->last_opcode_pos + 1 -
+ get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
+ /* for consistency we free the previous atom which is
+ JS_ATOM_empty_string */
+ atom = get_u32(fd->byte_code.buf + define_class_pos + 1);
+ JS_FreeAtom(s->ctx, atom);
+ put_u32(fd->byte_code.buf + define_class_pos + 1,
+ JS_DupAtom(s->ctx, name));
+ fd->last_opcode_pos = -1;
+ }
+}
+
+static void set_object_name_computed(JSParseState *s)
+{
+ JSFunctionDef *fd = s->cur_func;
+ int opcode;
+
+ opcode = get_prev_opcode(fd);
+ if (opcode == OP_set_name) {
+ /* XXX: should free atom after OP_set_name? */
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+ emit_op(s, OP_set_name_computed);
+ } else if (opcode == OP_set_class_name) {
+ int define_class_pos;
+ define_class_pos = fd->last_opcode_pos + 1 -
+ get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
+ fd->byte_code.buf[define_class_pos] = OP_define_class_computed;
+ fd->last_opcode_pos = -1;
+ }
+}
+
+static warn_unused int js_parse_object_literal(JSParseState *s)
+{
+ JSAtom name = JS_ATOM_NULL;
+ const uint8_t *start_ptr;
+ int start_line, prop_type;
+ BOOL has_proto;
+
+ if (next_token(s))
+ goto fail;
+ /* XXX: add an initial length that will be patched back */
+ emit_op(s, OP_object);
+ has_proto = FALSE;
+ while (s->token.val != '}') {
+ /* specific case for getter/setter */
+ start_ptr = s->token.ptr;
+ start_line = s->token.line_num;
+
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (next_token(s))
+ return -1;
+ if (js_parse_assign_expr(s))
+ return -1;
+ emit_op(s, OP_null); /* dummy excludeList */
+ emit_op(s, OP_copy_data_properties);
+ emit_u8(s, 2 | (1 << 2) | (0 << 5));
+ emit_op(s, OP_drop); /* pop excludeList */
+ emit_op(s, OP_drop); /* pop src object */
+ goto next;
+ }
+
+ prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);
+ if (prop_type < 0)
+ goto fail;
+
+ if (prop_type == PROP_TYPE_VAR) {
+ /* shortcut for x: x */
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ emit_op(s, OP_define_field);
+ emit_atom(s, name);
+ } else if (s->token.val == '(') {
+ BOOL is_getset = (prop_type == PROP_TYPE_GET ||
+ prop_type == PROP_TYPE_SET);
+ JSParseFunctionEnum func_type;
+ JSFunctionKindEnum func_kind;
+ int op_flags;
+
+ func_kind = JS_FUNC_NORMAL;
+ if (is_getset) {
+ func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;
+ } else {
+ func_type = JS_PARSE_FUNC_METHOD;
+ if (prop_type == PROP_TYPE_STAR)
+ func_kind = JS_FUNC_GENERATOR;
+ else if (prop_type == PROP_TYPE_ASYNC)
+ func_kind = JS_FUNC_ASYNC;
+ else if (prop_type == PROP_TYPE_ASYNC_STAR)
+ func_kind = JS_FUNC_ASYNC_GENERATOR;
+ }
+ if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,
+ start_ptr, start_line))
+ goto fail;
+ if (name == JS_ATOM_NULL) {
+ emit_op(s, OP_define_method_computed);
+ } else {
+ emit_op(s, OP_define_method);
+ emit_atom(s, name);
+ }
+ if (is_getset) {
+ op_flags = OP_DEFINE_METHOD_GETTER +
+ prop_type - PROP_TYPE_GET;
+ } else {
+ op_flags = OP_DEFINE_METHOD_METHOD;
+ }
+ emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);
+ } else {
+ if (js_parse_expect(s, ':'))
+ goto fail;
+ if (js_parse_assign_expr(s))
+ goto fail;
+ if (name == JS_ATOM_NULL) {
+ set_object_name_computed(s);
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_drop);
+ } else if (name == JS_ATOM___proto__) {
+ if (has_proto) {
+ js_parse_error(s, "duplicate __proto__ property name");
+ goto fail;
+ }
+ emit_op(s, OP_set_proto);
+ has_proto = TRUE;
+ } else {
+ set_object_name(s, name);
+ emit_op(s, OP_define_field);
+ emit_atom(s, name);
+ }
+ }
+ JS_FreeAtom(s->ctx, name);
+ next:
+ name = JS_ATOM_NULL;
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ goto fail;
+ }
+ if (js_parse_expect(s, '}'))
+ goto fail;
+ return 0;
+ fail:
+ JS_FreeAtom(s->ctx, name);
+ return -1;
+}
+
+/* allow the 'in' binary operator */
+#define PF_IN_ACCEPTED (1 << 0)
+/* allow function calls parsing in js_parse_postfix_expr() */
+#define PF_POSTFIX_CALL (1 << 1)
+/* allow arrow functions parsing in js_parse_postfix_expr() */
+#define PF_ARROW_FUNC (1 << 2)
+/* allow the exponentiation operator in js_parse_unary() */
+#define PF_POW_ALLOWED (1 << 3)
+/* forbid the exponentiation operator in js_parse_unary() */
+#define PF_POW_FORBIDDEN (1 << 4)
+
+static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags);
+
+static warn_unused int js_parse_left_hand_side_expr(JSParseState *s)
+{
+ return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
+}
+
+/* XXX: could generate specific bytecode */
+static warn_unused int js_parse_class_default_ctor(JSParseState *s,
+ BOOL has_super,
+ JSFunctionDef **pfd)
+{
+ JSParsePos pos;
+ const char *str;
+ int ret, line_num;
+ JSParseFunctionEnum func_type;
+ const uint8_t *saved_buf_end;
+
+ js_parse_get_pos(s, &pos);
+ if (has_super) {
+ /* spec change: no argument evaluation */
+ str = "(){super(...arguments);}";
+ func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
+ } else {
+ str = "(){}";
+ func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
+ }
+ line_num = s->token.line_num;
+ saved_buf_end = s->buf_end;
+ s->buf_ptr = (uint8_t *)str;
+ s->buf_end = (uint8_t *)(str + strlen(str));
+ ret = next_token(s);
+ if (!ret) {
+ ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,
+ JS_ATOM_NULL, (uint8_t *)str,
+ line_num, JS_PARSE_EXPORT_NONE, pfd);
+ }
+ s->buf_end = saved_buf_end;
+ ret |= js_parse_seek_token(s, &pos);
+ return ret;
+}
+
+/* find field in the current scope */
+static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,
+ JSAtom name, int scope_level)
+{
+ int idx;
+ idx = fd->scopes[scope_level].first;
+ while (idx != -1) {
+ if (fd->vars[idx].scope_level != scope_level)
+ break;
+ if (fd->vars[idx].var_name == name)
+ return idx;
+ idx = fd->vars[idx].scope_next;
+ }
+ return -1;
+}
+
+/* initialize the class fields, called by the constructor. Note:
+ super() can be called in an arrow function, so <this> and
+ <class_fields_init> can be variable references */
+static void emit_class_field_init(JSParseState *s)
+{
+ int label_next;
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_class_fields_init);
+ emit_u16(s, s->cur_func->scope_level);
+
+ /* no need to call the class field initializer if not defined */
+ emit_op(s, OP_dup);
+ label_next = emit_goto(s, OP_if_false, -1);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_swap);
+
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+
+ emit_label(s, label_next);
+ emit_op(s, OP_drop);
+}
+
+/* build a private setter function name from the private getter name */
+static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
+{
+ return js_atom_concat_str(ctx, name, "<set>");
+}
+
+typedef struct {
+ JSFunctionDef *fields_init_fd;
+ int computed_fields_count;
+ BOOL has_brand;
+ int brand_push_pos;
+} ClassFieldsDef;
+
+static warn_unused int emit_class_init_start(JSParseState *s,
+ ClassFieldsDef *cf)
+{
+ int label_add_brand;
+
+ cf->fields_init_fd = js_parse_function_class_fields_init(s);
+ if (!cf->fields_init_fd)
+ return -1;
+
+ s->cur_func = cf->fields_init_fd;
+
+ /* XXX: would be better to add the code only if needed, maybe in a
+ later pass */
+ emit_op(s, OP_push_false); /* will be patched later */
+ cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
+ label_add_brand = emit_goto(s, OP_if_false, -1);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_home_object);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_add_brand);
+
+ emit_label(s, label_add_brand);
+
+ s->cur_func = s->cur_func->parent;
+ return 0;
+}
+
+static warn_unused int add_brand(JSParseState *s, ClassFieldsDef *cf)
+{
+ if (!cf->has_brand) {
+ /* define the brand field in 'this' of the initializer */
+ if (!cf->fields_init_fd) {
+ if (emit_class_init_start(s, cf))
+ return -1;
+ }
+ /* patch the start of the function to enable the OP_add_brand code */
+ cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
+
+ cf->has_brand = TRUE;
+ }
+ return 0;
+}
+
+static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
+{
+ int cpool_idx;
+
+ s->cur_func = cf->fields_init_fd;
+ emit_op(s, OP_return_undef);
+ s->cur_func = s->cur_func->parent;
+
+ cpool_idx = cpool_add(s, JS_NULL);
+ cf->fields_init_fd->parent_cpool_idx = cpool_idx;
+ emit_op(s, OP_fclosure);
+ emit_u32(s, cpool_idx);
+ emit_op(s, OP_set_home_object);
+}
+
+
+static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr,
+ JSParseExportEnum export_flag)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;
+ JSAtom class_var_name = JS_ATOM_NULL;
+ JSFunctionDef *method_fd, *ctor_fd;
+ int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;
+ int class_flags = 0, i, define_class_offset;
+ BOOL is_static, is_private;
+ const uint8_t *class_start_ptr = s->token.ptr;
+ const uint8_t *start_ptr;
+ ClassFieldsDef class_fields[2];
+
+ /* classes are parsed and executed in strict mode */
+ saved_js_mode = fd->js_mode;
+ fd->js_mode |= JS_MODE_STRICT;
+ if (next_token(s))
+ goto fail;
+ if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ class_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail;
+ } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {
+ js_parse_error(s, "class statement requires a name");
+ goto fail;
+ }
+ if (!is_class_expr) {
+ if (class_name == JS_ATOM_NULL)
+ class_var_name = JS_ATOM__default_; /* export default */
+ else
+ class_var_name = class_name;
+ class_var_name = JS_DupAtom(ctx, class_var_name);
+ }
+
+ push_scope(s);
+
+ if (s->token.val == TOK_EXTENDS) {
+ class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;
+ if (next_token(s))
+ goto fail;
+ if (js_parse_left_hand_side_expr(s))
+ goto fail;
+ } else {
+ emit_op(s, OP_undefined);
+ }
+
+ /* add a 'const' definition for the class name */
+ if (class_name != JS_ATOM_NULL) {
+ class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);
+ if (class_name_var_idx < 0)
+ goto fail;
+ }
+
+ if (js_parse_expect(s, '{'))
+ goto fail;
+
+ /* this scope contains the private fields */
+ push_scope(s);
+
+ emit_op(s, OP_push_const);
+ ctor_cpool_offset = fd->byte_code.size;
+ emit_u32(s, 0); /* will be patched at the end of the class parsing */
+
+ if (class_name == JS_ATOM_NULL) {
+ if (class_var_name != JS_ATOM_NULL)
+ class_name1 = JS_ATOM_default;
+ else
+ class_name1 = JS_ATOM_empty_string;
+ } else {
+ class_name1 = class_name;
+ }
+
+ emit_op(s, OP_define_class);
+ emit_atom(s, class_name1);
+ emit_u8(s, class_flags);
+ define_class_offset = fd->last_opcode_pos;
+
+ for(i = 0; i < 2; i++) {
+ ClassFieldsDef *cf = &class_fields[i];
+ cf->fields_init_fd = NULL;
+ cf->computed_fields_count = 0;
+ cf->has_brand = FALSE;
+ }
+
+ ctor_fd = NULL;
+ while (s->token.val != '}') {
+ if (s->token.val == ';') {
+ if (next_token(s))
+ goto fail;
+ continue;
+ }
+ is_static = (s->token.val == TOK_STATIC);
+ prop_type = -1;
+ if (is_static) {
+ if (next_token(s))
+ goto fail;
+ /* allow "static" field name */
+ if (s->token.val == ';' || s->token.val == '=') {
+ is_static = FALSE;
+ name = JS_DupAtom(ctx, JS_ATOM_static);
+ prop_type = PROP_TYPE_IDENT;
+ }
+ }
+ if (is_static)
+ emit_op(s, OP_swap);
+ start_ptr = s->token.ptr;
+ if (prop_type < 0) {
+ prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);
+ if (prop_type < 0)
+ goto fail;
+ }
+ is_private = prop_type & PROP_TYPE_PRIVATE;
+ prop_type &= ~PROP_TYPE_PRIVATE;
+
+ if ((name == JS_ATOM_constructor && !is_static &&
+ prop_type != PROP_TYPE_IDENT) ||
+ (name == JS_ATOM_prototype && is_static) ||
+ name == JS_ATOM_hash_constructor) {
+ js_parse_error(s, "invalid method name");
+ goto fail;
+ }
+ if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {
+ BOOL is_set = prop_type - PROP_TYPE_GET;
+ JSFunctionDef *method_fd;
+
+ if (is_private) {
+ int idx, var_kind;
+ idx = find_private_class_field(ctx, fd, name, fd->scope_level);
+ if (idx >= 0) {
+ var_kind = fd->vars[idx].var_kind;
+ if (var_kind == JS_VAR_PRIVATE_FIELD ||
+ var_kind == JS_VAR_PRIVATE_METHOD ||
+ var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||
+ var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) {
+ goto private_field_already_defined;
+ }
+ fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;
+ } else {
+ if (add_private_class_field(s, fd, name,
+ JS_VAR_PRIVATE_GETTER + is_set) < 0)
+ goto fail;
+ }
+ if (add_brand(s, &class_fields[is_static]) < 0)
+ goto fail;
+ }
+
+ if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ start_ptr, s->token.line_num,
+ JS_PARSE_EXPORT_NONE, &method_fd))
+ goto fail;
+ if (is_private) {
+ method_fd->need_home_object = TRUE; /* needed for brand check */
+ emit_op(s, OP_set_home_object);
+ /* XXX: missing function name */
+ emit_op(s, OP_scope_put_var_init);
+ if (is_set) {
+ JSAtom setter_name;
+ int ret;
+
+ setter_name = get_private_setter_name(ctx, name);
+ if (setter_name == JS_ATOM_NULL)
+ goto fail;
+ emit_atom(s, setter_name);
+ ret = add_private_class_field(s, fd, setter_name,
+ JS_VAR_PRIVATE_SETTER);
+ JS_FreeAtom(ctx, setter_name);
+ if (ret < 0)
+ goto fail;
+ } else {
+ emit_atom(s, name);
+ }
+ emit_u16(s, s->cur_func->scope_level);
+ } else {
+ if (name == JS_ATOM_NULL) {
+ emit_op(s, OP_define_method_computed);
+ } else {
+ emit_op(s, OP_define_method);
+ emit_atom(s, name);
+ }
+ emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);
+ }
+ } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {
+ ClassFieldsDef *cf = &class_fields[is_static];
+ JSAtom field_var_name = JS_ATOM_NULL;
+
+ /* class field */
+
+ /* XXX: spec: not consistent with method name checks */
+ if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {
+ js_parse_error(s, "invalid field name");
+ goto fail;
+ }
+
+ if (is_private) {
+ if (find_private_class_field(ctx, fd, name,
+ fd->scope_level) >= 0) {
+ goto private_field_already_defined;
+ }
+ if (add_private_class_field(s, fd, name,
+ JS_VAR_PRIVATE_FIELD) < 0)
+ goto fail;
+ emit_op(s, OP_private_symbol);
+ emit_atom(s, name);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+
+ if (!cf->fields_init_fd) {
+ if (emit_class_init_start(s, cf))
+ goto fail;
+ }
+ if (name == JS_ATOM_NULL ) {
+ /* save the computed field name into a variable */
+ field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);
+ if (field_var_name == JS_ATOM_NULL)
+ goto fail;
+ if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {
+ JS_FreeAtom(ctx, field_var_name);
+ goto fail;
+ }
+ emit_op(s, OP_to_propkey);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, field_var_name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+ s->cur_func = cf->fields_init_fd;
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ if (name == JS_ATOM_NULL) {
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, field_var_name);
+ emit_u16(s, s->cur_func->scope_level);
+ cf->computed_fields_count++;
+ JS_FreeAtom(ctx, field_var_name);
+ } else if (is_private) {
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+
+ if (s->token.val == '=') {
+ if (next_token(s))
+ goto fail;
+ if (js_parse_assign_expr(s))
+ goto fail;
+ } else {
+ emit_op(s, OP_undefined);
+ }
+ if (is_private) {
+ set_object_name_computed(s);
+ emit_op(s, OP_define_private_field);
+ } else if (name == JS_ATOM_NULL) {
+ set_object_name_computed(s);
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_drop);
+ } else {
+ set_object_name(s, name);
+ emit_op(s, OP_define_field);
+ emit_atom(s, name);
+ }
+ s->cur_func = s->cur_func->parent;
+ if (js_parse_expect_semi(s))
+ goto fail;
+ } else {
+ JSParseFunctionEnum func_type;
+ JSFunctionKindEnum func_kind;
+
+ func_type = JS_PARSE_FUNC_METHOD;
+ func_kind = JS_FUNC_NORMAL;
+ if (prop_type == PROP_TYPE_STAR) {
+ func_kind = JS_FUNC_GENERATOR;
+ } else if (prop_type == PROP_TYPE_ASYNC) {
+ func_kind = JS_FUNC_ASYNC;
+ } else if (prop_type == PROP_TYPE_ASYNC_STAR) {
+ func_kind = JS_FUNC_ASYNC_GENERATOR;
+ } else if (name == JS_ATOM_constructor && !is_static) {
+ if (ctor_fd) {
+ js_parse_error(s, "property constructor appears more than once");
+ goto fail;
+ }
+ if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)
+ func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
+ else
+ func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
+ }
+ if (is_private) {
+ if (add_brand(s, &class_fields[is_static]) < 0)
+ goto fail;
+ }
+ if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd))
+ goto fail;
+ if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||
+ func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
+ ctor_fd = method_fd;
+ } else if (is_private) {
+ method_fd->need_home_object = TRUE; /* needed for brand check */
+ if (find_private_class_field(ctx, fd, name,
+ fd->scope_level) >= 0) {
+ private_field_already_defined:
+ js_parse_error(s, "private class field is already defined");
+ goto fail;
+ }
+ if (add_private_class_field(s, fd, name,
+ JS_VAR_PRIVATE_METHOD) < 0)
+ goto fail;
+ emit_op(s, OP_set_home_object);
+ emit_op(s, OP_set_name);
+ emit_atom(s, name);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ } else {
+ if (name == JS_ATOM_NULL) {
+ emit_op(s, OP_define_method_computed);
+ } else {
+ emit_op(s, OP_define_method);
+ emit_atom(s, name);
+ }
+ emit_u8(s, OP_DEFINE_METHOD_METHOD);
+ }
+ }
+ if (is_static)
+ emit_op(s, OP_swap);
+ JS_FreeAtom(ctx, name);
+ name = JS_ATOM_NULL;
+ }
+
+ if (s->token.val != '}') {
+ js_parse_error(s, "expecting '%c'", '}');
+ goto fail;
+ }
+
+ if (!ctor_fd) {
+ if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))
+ goto fail;
+ }
+ /* patch the constant pool index for the constructor */
+ put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);
+
+ /* store the class source code in the constructor. */
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ js_free(ctx, ctor_fd->source);
+ ctor_fd->source_len = s->buf_ptr - class_start_ptr;
+ ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,
+ ctor_fd->source_len);
+ if (!ctor_fd->source)
+ goto fail;
+ }
+
+ /* consume the '}' */
+ if (next_token(s))
+ goto fail;
+
+ /* store the function to initialize the fields to that it can be
+ referenced by the constructor */
+ {
+ ClassFieldsDef *cf = &class_fields[0];
+ int var_idx;
+
+ var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
+ JS_VAR_DEF_CONST);
+ if (var_idx < 0)
+ goto fail;
+ if (cf->fields_init_fd) {
+ emit_class_init_end(s, cf);
+ } else {
+ emit_op(s, OP_undefined);
+ }
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, JS_ATOM_class_fields_init);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+
+ /* drop the prototype */
+ emit_op(s, OP_drop);
+
+ /* initialize the static fields */
+ if (class_fields[1].fields_init_fd != NULL) {
+ ClassFieldsDef *cf = &class_fields[1];
+ emit_op(s, OP_dup);
+ emit_class_init_end(s, cf);
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ emit_op(s, OP_drop);
+ }
+
+ if (class_name != JS_ATOM_NULL) {
+ /* store the class name in the scoped class name variable (it
+ is independent from the class statement variable
+ definition) */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, class_name);
+ emit_u16(s, fd->scope_level);
+ }
+ pop_scope(s);
+ pop_scope(s);
+
+ /* the class statements have a block level scope */
+ if (class_var_name != JS_ATOM_NULL) {
+ if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)
+ goto fail;
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, class_var_name);
+ emit_u16(s, fd->scope_level);
+ } else {
+ if (class_name == JS_ATOM_NULL) {
+ /* cannot use OP_set_name because the name of the class
+ must be defined before the static initializers are
+ executed */
+ emit_op(s, OP_set_class_name);
+ emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);
+ }
+ }
+
+ if (export_flag != JS_PARSE_EXPORT_NONE) {
+ if (!add_export_entry(s, fd->module,
+ class_var_name,
+ export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,
+ JS_EXPORT_TYPE_LOCAL))
+ goto fail;
+ }
+
+ JS_FreeAtom(ctx, class_name);
+ JS_FreeAtom(ctx, class_var_name);
+ fd->js_mode = saved_js_mode;
+ return 0;
+ fail:
+ JS_FreeAtom(ctx, name);
+ JS_FreeAtom(ctx, class_name);
+ JS_FreeAtom(ctx, class_var_name);
+ fd->js_mode = saved_js_mode;
+ return -1;
+}
+
+static warn_unused int js_parse_array_literal(JSParseState *s)
+{
+ uint32_t idx;
+ BOOL need_length;
+
+ if (next_token(s))
+ return -1;
+ /* small regular arrays are created on the stack */
+ idx = 0;
+ while (s->token.val != ']' && idx < 32) {
+ if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)
+ break;
+ if (js_parse_assign_expr(s))
+ return -1;
+ idx++;
+ /* accept trailing comma */
+ if (s->token.val == ',') {
+ if (next_token(s))
+ return -1;
+ } else
+ if (s->token.val != ']')
+ goto done;
+ }
+ emit_op(s, OP_array_from);
+ emit_u16(s, idx);
+
+ /* larger arrays and holes are handled with explicit indices */
+ need_length = FALSE;
+ while (s->token.val != ']' && idx < 0x7fffffff) {
+ if (s->token.val == TOK_ELLIPSIS)
+ break;
+ need_length = TRUE;
+ if (s->token.val != ',') {
+ if (js_parse_assign_expr(s))
+ return -1;
+ emit_op(s, OP_define_field);
+ emit_u32(s, JS_AtomFromUInt32(idx));
+ need_length = FALSE;
+ }
+ idx++;
+ /* accept trailing comma */
+ if (s->token.val == ',') {
+ if (next_token(s))
+ return -1;
+ }
+ }
+ if (s->token.val == ']') {
+ if (need_length) {
+ /* Set the length: Cannot use OP_define_field because
+ length is not configurable */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_push_i32);
+ emit_u32(s, idx);
+ emit_op(s, OP_put_field);
+ emit_atom(s, JS_ATOM_length);
+ }
+ goto done;
+ }
+
+ /* huge arrays and spread elements require a dynamic index on the stack */
+ emit_op(s, OP_push_i32);
+ emit_u32(s, idx);
+
+ /* stack has array, index */
+ while (s->token.val != ']') {
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (next_token(s))
+ return -1;
+ if (js_parse_assign_expr(s))
+ return -1;
+#if 1
+ emit_op(s, OP_append);
+#else
+ int label_next, label_done;
+ label_next = new_label(s);
+ label_done = new_label(s);
+ /* enumerate object */
+ emit_op(s, OP_for_of_start);
+ emit_op(s, OP_rot5l);
+ emit_op(s, OP_rot5l);
+ emit_label(s, label_next);
+ /* on stack: enum_rec array idx */
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 2);
+ emit_goto(s, OP_if_true, label_done);
+ /* append element */
+ /* enum_rec array idx val -> enum_rec array new_idx */
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_inc);
+ emit_goto(s, OP_goto, label_next);
+ emit_label(s, label_done);
+ /* close enumeration */
+ emit_op(s, OP_drop); /* drop undef val */
+ emit_op(s, OP_nip1); /* drop enum_rec */
+ emit_op(s, OP_nip1);
+ emit_op(s, OP_nip1);
+#endif
+ } else {
+ need_length = TRUE;
+ if (s->token.val != ',') {
+ if (js_parse_assign_expr(s))
+ return -1;
+ /* a idx val */
+ emit_op(s, OP_define_array_el);
+ need_length = FALSE;
+ }
+ emit_op(s, OP_inc);
+ }
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ return -1;
+ }
+ if (need_length) {
+ /* Set the length: cannot use OP_define_field because
+ length is not configurable */
+ emit_op(s, OP_dup1); /* array length - array array length */
+ emit_op(s, OP_put_field);
+ emit_atom(s, JS_ATOM_length);
+ } else {
+ emit_op(s, OP_drop); /* array length - array */
+ }
+done:
+ return js_parse_expect(s, ']');
+}
+
+/* XXX: remove */
+static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
+{
+ /* check if scope chain contains a with statement */
+ while (s) {
+ int scope_idx = s->scopes[scope_level].first;
+ while (scope_idx >= 0) {
+ JSVarDef *vd = &s->vars[scope_idx];
+
+ if (vd->var_name == JS_ATOM__with_)
+ return TRUE;
+ scope_idx = vd->scope_next;
+ }
+ /* check parent scopes */
+ scope_level = s->parent_scope_level;
+ s = s->parent;
+ }
+ return FALSE;
+}
+
+static warn_unused int get_lvalue(JSParseState *s, int *popcode, int *pscope,
+ JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
+ int tok)
+{
+ JSFunctionDef *fd;
+ int opcode, scope, label, depth;
+ JSAtom name;
+
+ /* we check the last opcode to get the lvalue type */
+ fd = s->cur_func;
+ scope = 0;
+ name = JS_ATOM_NULL;
+ label = -1;
+ depth = 0;
+ switch(opcode = get_prev_opcode(fd)) {
+ case OP_scope_get_var:
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
+ if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&
+ (fd->js_mode & JS_MODE_STRICT)) {
+ return js_parse_error(s, "invalid lvalue in strict mode");
+ }
+ if (name == JS_ATOM_this || name == JS_ATOM_new_target)
+ goto invalid_lvalue;
+ depth = 2; /* will generate OP_get_ref_value */
+ break;
+ case OP_get_field:
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ depth = 1;
+ break;
+ case OP_scope_get_private_field:
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
+ depth = 1;
+ break;
+ case OP_get_array_el:
+ depth = 2;
+ break;
+ case OP_get_super_value:
+ depth = 3;
+ break;
+ default:
+ invalid_lvalue:
+ if (tok == TOK_FOR) {
+ return js_parse_error(s, "invalid for in/of left hand-side");
+ } else if (tok == TOK_INC || tok == TOK_DEC) {
+ return js_parse_error(s, "invalid increment/decrement operand");
+ } else if (tok == '[' || tok == '{') {
+ return js_parse_error(s, "invalid destructuring target");
+ } else {
+ return js_parse_error(s, "invalid assignment left-hand side");
+ }
+ }
+ /* remove the last opcode */
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+
+ if (keep) {
+ /* get the value but keep the object/fields on the stack */
+ switch(opcode) {
+ case OP_scope_get_var:
+ label = new_label(s);
+ emit_op(s, OP_scope_make_ref);
+ emit_atom(s, name);
+ emit_u32(s, label);
+ emit_u16(s, scope);
+ update_label(fd, label, 1);
+ emit_op(s, OP_get_ref_value);
+ opcode = OP_get_ref_value;
+ break;
+ case OP_get_field:
+ emit_op(s, OP_get_field2);
+ emit_atom(s, name);
+ break;
+ case OP_scope_get_private_field:
+ emit_op(s, OP_scope_get_private_field2);
+ emit_atom(s, name);
+ emit_u16(s, scope);
+ break;
+ case OP_get_array_el:
+ /* XXX: replace by a single opcode ? */
+ emit_op(s, OP_to_propkey2);
+ emit_op(s, OP_dup2);
+ emit_op(s, OP_get_array_el);
+ break;
+ case OP_get_super_value:
+ emit_op(s, OP_to_propkey);
+ emit_op(s, OP_dup3);
+ emit_op(s, OP_get_super_value);
+ break;
+ default:
+ abort();
+ }
+ } else {
+ switch(opcode) {
+ case OP_scope_get_var:
+ label = new_label(s);
+ emit_op(s, OP_scope_make_ref);
+ emit_atom(s, name);
+ emit_u32(s, label);
+ emit_u16(s, scope);
+ update_label(fd, label, 1);
+ opcode = OP_get_ref_value;
+ break;
+ case OP_get_array_el:
+ emit_op(s, OP_to_propkey2);
+ break;
+ case OP_get_super_value:
+ emit_op(s, OP_to_propkey);
+ break;
+ }
+ }
+
+ *popcode = opcode;
+ *pscope = scope;
+ /* name has refcount for OP_get_field and OP_get_ref_value,
+ and JS_ATOM_NULL for other opcodes */
+ *pname = name;
+ *plabel = label;
+ if (pdepth)
+ *pdepth = depth;
+ return 0;
+}
+
+typedef enum {
+ PUT_LVALUE_NOKEEP, /* [depth] v -> */
+ PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently
+ just disable optimizations) */
+ PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */
+ PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */
+ PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */
+} PutLValueEnum;
+
+/* name has a live reference. 'is_let' is only used with opcode =
+ OP_scope_get_var which is never generated by get_lvalue(). */
+static void put_lvalue(JSParseState *s, int opcode, int scope,
+ JSAtom name, int label, PutLValueEnum special,
+ BOOL is_let)
+{
+ switch(opcode) {
+ case OP_get_field:
+ case OP_scope_get_private_field:
+ /* depth = 1 */
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
+ emit_op(s, OP_insert2); /* obj v -> v obj v */
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
+ emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_swap);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case OP_get_array_el:
+ case OP_get_ref_value:
+ /* depth = 2 */
+ if (opcode == OP_get_ref_value) {
+ JS_FreeAtom(s->ctx, name);
+ emit_label(s, label);
+ }
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ emit_op(s, OP_nop); /* will trigger optimization */
+ break;
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
+ emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
+ emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_rot3l);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case OP_get_super_value:
+ /* depth = 3 */
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
+ emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
+ emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_rot4l);
+ break;
+ default:
+ abort();
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch(opcode) {
+ case OP_scope_get_var: /* val -- */
+ assert(special == PUT_LVALUE_NOKEEP ||
+ special == PUT_LVALUE_NOKEEP_DEPTH);
+ emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
+ emit_u32(s, name); /* has refcount */
+ emit_u16(s, scope);
+ break;
+ case OP_get_field:
+ emit_op(s, OP_put_field);
+ emit_u32(s, name); /* name has refcount */
+ break;
+ case OP_scope_get_private_field:
+ emit_op(s, OP_scope_put_private_field);
+ emit_u32(s, name); /* name has refcount */
+ emit_u16(s, scope);
+ break;
+ case OP_get_array_el:
+ emit_op(s, OP_put_array_el);
+ break;
+ case OP_get_ref_value:
+ emit_op(s, OP_put_ref_value);
+ break;
+ case OP_get_super_value:
+ emit_op(s, OP_put_super_value);
+ break;
+ default:
+ abort();
+ }
+}
+
+static warn_unused int js_parse_expr_paren(JSParseState *s)
+{
+ if (js_parse_expect(s, '('))
+ return -1;
+ if (js_parse_expr(s))
+ return -1;
+ if (js_parse_expect(s, ')'))
+ return -1;
+ return 0;
+}
+
+static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return js_parse_error(s, "unsupported keyword: %s",
+ JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
+}
+
+static warn_unused int js_define_var(JSParseState *s, JSAtom name, int tok)
+{
+ JSFunctionDef *fd = s->cur_func;
+ JSVarDefEnum var_def_type;
+
+ if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
+ return js_parse_error(s, "yield is a reserved identifier");
+ }
+ if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)
+ && (fd->js_mode & JS_MODE_STRICT)) {
+ return js_parse_error(s, "invalid variable name in strict mode");
+ }
+ if ((name == JS_ATOM_let || name == JS_ATOM_undefined)
+ && (tok == TOK_LET || tok == TOK_CONST)) {
+ return js_parse_error(s, "invalid lexical variable name");
+ }
+ switch(tok) {
+ case TOK_LET:
+ var_def_type = JS_VAR_DEF_LET;
+ break;
+ case TOK_CONST:
+ var_def_type = JS_VAR_DEF_CONST;
+ break;
+ case TOK_VAR:
+ var_def_type = JS_VAR_DEF_VAR;
+ break;
+ case TOK_CATCH:
+ var_def_type = JS_VAR_DEF_CATCH;
+ break;
+ default:
+ abort();
+ }
+ if (define_var(s, fd, name, var_def_type) < 0)
+ return -1;
+ return 0;
+}
+
+static void js_emit_spread_code(JSParseState *s, int depth)
+{
+ int label_rest_next, label_rest_done;
+
+ /* XXX: could check if enum object is an actual array and optimize
+ slice extraction. enumeration record and target array are in a
+ different order from OP_append case. */
+ /* enum_rec xxx -- enum_rec xxx array 0 */
+ emit_op(s, OP_array_from);
+ emit_u16(s, 0);
+ emit_op(s, OP_push_i32);
+ emit_u32(s, 0);
+ emit_label(s, label_rest_next = new_label(s));
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 2 + depth);
+ label_rest_done = emit_goto(s, OP_if_true, -1);
+ /* array idx val -- array idx */
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_inc);
+ emit_goto(s, OP_goto, label_rest_next);
+ emit_label(s, label_rest_done);
+ /* enum_rec xxx array idx undef -- enum_rec xxx array */
+ emit_op(s, OP_drop);
+ emit_op(s, OP_drop);
+}
+
+static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)
+{
+ /* Check for duplicate parameter names */
+ JSFunctionDef *fd = s->cur_func;
+ int i;
+ for (i = 0; i < fd->arg_count; i++) {
+ if (fd->args[i].var_name == name)
+ goto duplicate;
+ }
+ for (i = 0; i < fd->var_count; i++) {
+ if (fd->vars[i].var_name == name)
+ goto duplicate;
+ }
+ return 0;
+
+duplicate:
+ return js_parse_error(s, "duplicate parameter names not allowed in this context");
+}
+
+static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
+{
+ JSAtom name;
+
+ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
+ || ((s->cur_func->js_mode & JS_MODE_STRICT) &&
+ (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {
+ js_parse_error(s, "invalid destructuring target");
+ return JS_ATOM_NULL;
+ }
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (is_arg && js_parse_check_duplicate_parameter(s, name))
+ goto fail;
+ if (next_token(s))
+ goto fail;
+
+ return name;
+fail:
+ JS_FreeAtom(s->ctx, name);
+ return JS_ATOM_NULL;
+}
+
+/* Return -1 if error, 0 if no initializer, 1 if an initializer is
+ present at the top level. */
+static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
+ int hasval, int has_ellipsis,
+ BOOL allow_initializer)
+{
+ int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;
+ int start_addr, assign_addr;
+ JSAtom prop_name, var_name;
+ int opcode, scope, tok1, skip_bits;
+ BOOL has_initializer;
+
+ if (has_ellipsis < 0) {
+ /* pre-parse destructuration target for spread detection */
+ js_parse_skip_parens_token(s, &skip_bits, FALSE);
+ has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;
+ }
+
+ label_parse = new_label(s);
+ label_assign = new_label(s);
+
+ start_addr = s->cur_func->byte_code.size;
+ if (hasval) {
+ /* consume value from the stack */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_strict_eq);
+ emit_goto(s, OP_if_true, label_parse);
+ emit_label(s, label_assign);
+ } else {
+ emit_goto(s, OP_goto, label_parse);
+ emit_label(s, label_assign);
+ /* leave value on the stack */
+ emit_op(s, OP_dup);
+ }
+ assign_addr = s->cur_func->byte_code.size;
+ if (s->token.val == '{') {
+ if (next_token(s))
+ return -1;
+ /* throw an exception if the value cannot be converted to an object */
+ emit_op(s, OP_to_object);
+ if (has_ellipsis) {
+ /* add excludeList on stack just below src object */
+ emit_op(s, OP_object);
+ emit_op(s, OP_swap);
+ }
+ while (s->token.val != '}') {
+ int prop_type;
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (!has_ellipsis) {
+ JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");
+ return -1;
+ }
+ if (next_token(s))
+ return -1;
+ if (tok) {
+ var_name = js_parse_destructuring_var(s, tok, is_arg);
+ if (var_name == JS_ATOM_NULL)
+ return -1;
+ opcode = OP_scope_get_var;
+ scope = s->cur_func->scope_level;
+ label_lvalue = -1;
+ depth_lvalue = 0;
+ } else {
+ if (js_parse_left_hand_side_expr(s))
+ return -1;
+
+ if (get_lvalue(s, &opcode, &scope, &var_name,
+ &label_lvalue, &depth_lvalue, FALSE, '{'))
+ return -1;
+ }
+ if (s->token.val != '}') {
+ js_parse_error(s, "assignment rest property must be last");
+ goto var_error;
+ }
+ emit_op(s, OP_object); /* target */
+ emit_op(s, OP_copy_data_properties);
+ emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));
+ goto set_val;
+ }
+ prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);
+ if (prop_type < 0)
+ return -1;
+ var_name = JS_ATOM_NULL;
+ opcode = OP_scope_get_var;
+ scope = s->cur_func->scope_level;
+ label_lvalue = -1;
+ depth_lvalue = 0;
+ if (prop_type == PROP_TYPE_IDENT) {
+ if (next_token(s))
+ goto prop_error;
+ if ((s->token.val == '[' || s->token.val == '{')
+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
+ tok1 == '=' || tok1 == '}')) {
+ if (prop_name == JS_ATOM_NULL) {
+ /* computed property name on stack */
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_to_propkey); /* avoid calling ToString twice */
+ emit_op(s, OP_perm3); /* TOS: src excludeList prop */
+ emit_op(s, OP_null); /* TOS: src excludeList prop null */
+ emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */
+ emit_op(s, OP_perm3); /* TOS: excludeList src prop */
+ }
+ /* get the computed property from the source object */
+ emit_op(s, OP_get_array_el2);
+ } else {
+ /* named property */
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_swap); /* TOS: src excludeList */
+ emit_op(s, OP_null); /* TOS: src excludeList null */
+ emit_op(s, OP_define_field); /* TOS: src excludeList */
+ emit_atom(s, prop_name);
+ emit_op(s, OP_swap); /* TOS: excludeList src */
+ }
+ /* get the named property from the source object */
+ emit_op(s, OP_get_field2);
+ emit_u32(s, prop_name);
+ }
+ if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0)
+ return -1;
+ if (s->token.val == '}')
+ break;
+ /* accept a trailing comma before the '}' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ continue;
+ }
+ if (prop_name == JS_ATOM_NULL) {
+ emit_op(s, OP_to_propkey2);
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_perm3);
+ emit_op(s, OP_null);
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_perm3);
+ }
+ /* source prop -- source source prop */
+ emit_op(s, OP_dup1);
+ } else {
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_swap);
+ emit_op(s, OP_null);
+ emit_op(s, OP_define_field);
+ emit_atom(s, prop_name);
+ emit_op(s, OP_swap);
+ }
+ /* source -- source source */
+ emit_op(s, OP_dup);
+ }
+ if (tok) {
+ var_name = js_parse_destructuring_var(s, tok, is_arg);
+ if (var_name == JS_ATOM_NULL)
+ goto prop_error;
+ } else {
+ if (js_parse_left_hand_side_expr(s))
+ goto prop_error;
+ lvalue:
+ if (get_lvalue(s, &opcode, &scope, &var_name,
+ &label_lvalue, &depth_lvalue, FALSE, '{'))
+ goto prop_error;
+ /* swap ref and lvalue object if any */
+ if (prop_name == JS_ATOM_NULL) {
+ switch(depth_lvalue) {
+ case 1:
+ /* source prop x -> x source prop */
+ emit_op(s, OP_rot3r);
+ break;
+ case 2:
+ /* source prop x y -> x y source prop */
+ emit_op(s, OP_swap2); /* t p2 s p1 */
+ break;
+ case 3:
+ /* source prop x y z -> x y z source prop */
+ emit_op(s, OP_rot5l);
+ emit_op(s, OP_rot5l);
+ break;
+ }
+ } else {
+ switch(depth_lvalue) {
+ case 1:
+ /* source x -> x source */
+ emit_op(s, OP_swap);
+ break;
+ case 2:
+ /* source x y -> x y source */
+ emit_op(s, OP_rot3l);
+ break;
+ case 3:
+ /* source x y z -> x y z source */
+ emit_op(s, OP_rot4l);
+ break;
+ }
+ }
+ }
+ if (prop_name == JS_ATOM_NULL) {
+ /* computed property name on stack */
+ /* XXX: should have OP_get_array_el2x with depth */
+ /* source prop -- val */
+ emit_op(s, OP_get_array_el);
+ } else {
+ /* named property */
+ /* XXX: should have OP_get_field2x with depth */
+ /* source -- val */
+ emit_op(s, OP_get_field);
+ emit_u32(s, prop_name);
+ }
+ } else {
+ /* prop_type = PROP_TYPE_VAR, cannot be a computed property */
+ if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))
+ goto prop_error;
+ if ((s->cur_func->js_mode & JS_MODE_STRICT) &&
+ (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {
+ js_parse_error(s, "invalid destructuring target");
+ goto prop_error;
+ }
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_swap);
+ emit_op(s, OP_null);
+ emit_op(s, OP_define_field);
+ emit_atom(s, prop_name);
+ emit_op(s, OP_swap);
+ }
+ if (!tok || tok == TOK_VAR) {
+ /* generate reference */
+ /* source -- source source */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, prop_name);
+ emit_u16(s, s->cur_func->scope_level);
+ goto lvalue;
+ }
+ var_name = JS_DupAtom(s->ctx, prop_name);
+ /* source -- source val */
+ emit_op(s, OP_get_field2);
+ emit_u32(s, prop_name);
+ }
+ set_val:
+ if (tok) {
+ if (js_define_var(s, var_name, tok))
+ goto var_error;
+ scope = s->cur_func->scope_level;
+ }
+ if (s->token.val == '=') { /* handle optional default value */
+ int label_hasval;
+ emit_op(s, OP_dup);
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_strict_eq);
+ label_hasval = emit_goto(s, OP_if_false, -1);
+ if (next_token(s))
+ goto var_error;
+ emit_op(s, OP_drop);
+ if (js_parse_assign_expr(s))
+ goto var_error;
+ if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
+ set_object_name(s, var_name);
+ emit_label(s, label_hasval);
+ }
+ /* store value into lvalue object */
+ put_lvalue(s, opcode, scope, var_name, label_lvalue,
+ PUT_LVALUE_NOKEEP_DEPTH,
+ (tok == TOK_CONST || tok == TOK_LET));
+ if (s->token.val == '}')
+ break;
+ /* accept a trailing comma before the '}' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ }
+ /* drop the source object */
+ emit_op(s, OP_drop);
+ if (has_ellipsis) {
+ emit_op(s, OP_drop); /* pop excludeList */
+ }
+ if (next_token(s))
+ return -1;
+ } else if (s->token.val == '[') {
+ BOOL has_spread;
+ int enum_depth;
+ BlockEnv block_env;
+
+ if (next_token(s))
+ return -1;
+ /* the block environment is only needed in generators in case
+ 'yield' triggers a 'return' */
+ push_break_entry(s->cur_func, &block_env,
+ JS_ATOM_NULL, -1, -1, 2);
+ block_env.has_iterator = TRUE;
+ emit_op(s, OP_for_of_start);
+ has_spread = FALSE;
+ while (s->token.val != ']') {
+ /* get the next value */
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (next_token(s))
+ return -1;
+ if (s->token.val == ',' || s->token.val == ']')
+ return js_parse_error(s, "missing binding pattern...");
+ has_spread = TRUE;
+ }
+ if (s->token.val == ',') {
+ /* do nothing, skip the value, has_spread is false */
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 0);
+ emit_op(s, OP_drop);
+ emit_op(s, OP_drop);
+ } else if ((s->token.val == '[' || s->token.val == '{')
+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
+ tok1 == '=' || tok1 == ']')) {
+ if (has_spread) {
+ if (tok1 == '=')
+ return js_parse_error(s, "rest element cannot have a default value");
+ js_emit_spread_code(s, 0);
+ } else {
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 0);
+ emit_op(s, OP_drop);
+ }
+ if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
+ return -1;
+ } else {
+ var_name = JS_ATOM_NULL;
+ enum_depth = 0;
+ if (tok) {
+ var_name = js_parse_destructuring_var(s, tok, is_arg);
+ if (var_name == JS_ATOM_NULL)
+ goto var_error;
+ if (js_define_var(s, var_name, tok))
+ goto var_error;
+ opcode = OP_scope_get_var;
+ scope = s->cur_func->scope_level;
+ } else {
+ if (js_parse_left_hand_side_expr(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &var_name,
+ &label_lvalue, &enum_depth, FALSE, '[')) {
+ return -1;
+ }
+ }
+ if (has_spread) {
+ js_emit_spread_code(s, enum_depth);
+ } else {
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, enum_depth);
+ emit_op(s, OP_drop);
+ }
+ if (s->token.val == '=' && !has_spread) {
+ /* handle optional default value */
+ int label_hasval;
+ emit_op(s, OP_dup);
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_strict_eq);
+ label_hasval = emit_goto(s, OP_if_false, -1);
+ if (next_token(s))
+ goto var_error;
+ emit_op(s, OP_drop);
+ if (js_parse_assign_expr(s))
+ goto var_error;
+ if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
+ set_object_name(s, var_name);
+ emit_label(s, label_hasval);
+ }
+ /* store value into lvalue object */
+ put_lvalue(s, opcode, scope, var_name,
+ label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,
+ (tok == TOK_CONST || tok == TOK_LET));
+ }
+ if (s->token.val == ']')
+ break;
+ if (has_spread)
+ return js_parse_error(s, "rest element must be the last one");
+ /* accept a trailing comma before the ']' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ }
+ /* close iterator object:
+ if completed, enum_obj has been replaced by undefined */
+ emit_op(s, OP_iterator_close);
+ pop_break_entry(s->cur_func);
+ if (next_token(s))
+ return -1;
+ } else {
+ return js_parse_error(s, "invalid assignment syntax");
+ }
+ if (s->token.val == '=' && allow_initializer) {
+ label_done = emit_goto(s, OP_goto, -1);
+ if (next_token(s))
+ return -1;
+ emit_label(s, label_parse);
+ if (hasval)
+ emit_op(s, OP_drop);
+ if (js_parse_assign_expr(s))
+ return -1;
+ emit_goto(s, OP_goto, label_assign);
+ emit_label(s, label_done);
+ has_initializer = TRUE;
+ } else {
+ /* normally hasval is true except if
+ js_parse_skip_parens_token() was wrong in the parsing */
+ // assert(hasval);
+ if (!hasval) {
+ js_parse_error(s, "too complicated destructuring expression");
+ return -1;
+ }
+ /* remove test and decrement label ref count */
+ memset(s->cur_func->byte_code.buf + start_addr, OP_nop,
+ assign_addr - start_addr);
+ s->cur_func->label_slots[label_parse].ref_count--;
+ has_initializer = FALSE;
+ }
+ return has_initializer;
+
+ prop_error:
+ JS_FreeAtom(s->ctx, prop_name);
+ var_error:
+ JS_FreeAtom(s->ctx, var_name);
+ return -1;
+}
+
+typedef enum FuncCallType {
+ FUNC_CALL_NORMAL,
+ FUNC_CALL_NEW,
+ FUNC_CALL_SUPER_CTOR,
+ FUNC_CALL_TEMPLATE,
+} FuncCallType;
+
+static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
+ int drop_count)
+{
+ int label_next, i;
+ if (*poptional_chaining_label < 0)
+ *poptional_chaining_label = new_label(s);
+ /* XXX: could be more efficient with a specific opcode */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined_or_null);
+ label_next = emit_goto(s, OP_if_false, -1);
+ for(i = 0; i < drop_count; i++)
+ emit_op(s, OP_drop);
+ emit_op(s, OP_undefined);
+ emit_goto(s, OP_goto, *poptional_chaining_label);
+ emit_label(s, label_next);
+}
+
+/* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */
+static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags)
+{
+ FuncCallType call_type;
+ int optional_chaining_label;
+ BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
+
+ call_type = FUNC_CALL_NORMAL;
+ switch(s->token.val) {
+ case TOK_NUMBER:
+ {
+ JSValue val;
+ val = s->token.u.num.val;
+
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
+ emit_op(s, OP_push_i32);
+ emit_u32(s, JS_VALUE_GET_INT(val));
+ } else
+#ifdef CONFIG_BIGNUM
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
+ slimb_t e;
+ int ret;
+
+ /* need a runtime conversion */
+ /* XXX: could add a cache and/or do it once at
+ the start of the function */
+ if (emit_push_const(s, val, 0) < 0)
+ return -1;
+ e = s->token.u.num.exponent;
+ if (e == (int32_t)e) {
+ emit_op(s, OP_push_i32);
+ emit_u32(s, e);
+ } else {
+ val = JS_NewBigInt64_1(s->ctx, e);
+ if (JS_IsException(val))
+ return -1;
+ ret = emit_push_const(s, val, 0);
+ JS_FreeValue(s->ctx, val);
+ if (ret < 0)
+ return -1;
+ }
+ emit_op(s, OP_mul_pow10);
+ } else
+#endif
+ {
+ if (emit_push_const(s, val, 0) < 0)
+ return -1;
+ }
+ }
+ if (next_token(s))
+ return -1;
+ break;
+ case TOK_TEMPLATE:
+ if (js_parse_template(s, 0, NULL))
+ return -1;
+ break;
+ case TOK_STRING:
+ if (emit_push_const(s, s->token.u.str.str, 1))
+ return -1;
+ if (next_token(s))
+ return -1;
+ break;
+
+ case TOK_DIV_ASSIGN:
+ s->buf_ptr -= 2;
+ goto parse_regexp;
+ case '/':
+ s->buf_ptr--;
+ parse_regexp:
+ {
+ JSValue str;
+ int ret, backtrace_flags;
+ if (!s->ctx->compile_regexp)
+ return js_parse_error(s, "RegExp are not supported");
+ /* the previous token is '/' or '/=', so no need to free */
+ if (js_parse_regexp(s))
+ return -1;
+ ret = emit_push_const(s, s->token.u.regexp.body, 0);
+ str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,
+ s->token.u.regexp.flags);
+ if (JS_IsException(str)) {
+ /* add the line number info */
+ backtrace_flags = 0;
+ if (s->cur_func && s->cur_func->backtrace_barrier)
+ backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
+ build_backtrace(s->ctx, s->ctx->rt->current_exception,
+ s->filename, s->token.line_num,
+ backtrace_flags);
+ return -1;
+ }
+ ret = emit_push_const(s, str, 0);
+ JS_FreeValue(s->ctx, str);
+ if (ret)
+ return -1;
+ /* we use a specific opcode to be sure the correct
+ function is called (otherwise the bytecode would have
+ to be verified by the RegExp constructor) */
+ emit_op(s, OP_regexp);
+ if (next_token(s))
+ return -1;
+ }
+ break;
+ case '(':
+ if ((parse_flags & PF_ARROW_FUNC) &&
+ js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ return -1;
+ } else {
+ if (js_parse_expr_paren(s))
+ return -1;
+ }
+ break;
+ case TOK_FUNCTION:
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ return -1;
+ break;
+ case TOK_CLASS:
+ if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))
+ return -1;
+ break;
+ case TOK_NULL:
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_null);
+ break;
+ case TOK_THIS:
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+ break;
+ case TOK_FALSE:
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_push_false);
+ break;
+ case TOK_TRUE:
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_push_true);
+ break;
+ case TOK_IDENT:
+ {
+ JSAtom name;
+ if (s->token.u.ident.is_reserved) {
+ return js_parse_error_reserved_identifier(s);
+ }
+ if ((parse_flags & PF_ARROW_FUNC) &&
+ peek_token(s, TRUE) == TOK_ARROW) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ return -1;
+ } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) != '\n') {
+ const uint8_t *source_ptr;
+ int source_line_num;
+
+ source_ptr = s->token.ptr;
+ source_line_num = s->token.line_num;
+ if (next_token(s))
+ return -1;
+ if (s->token.val == TOK_FUNCTION) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
+ JS_FUNC_ASYNC, JS_ATOM_NULL,
+ source_ptr, source_line_num))
+ return -1;
+ } else if ((parse_flags & PF_ARROW_FUNC) &&
+ ((s->token.val == '(' &&
+ js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||
+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
+ peek_token(s, TRUE) == TOK_ARROW))) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
+ JS_FUNC_ASYNC, JS_ATOM_NULL,
+ source_ptr, source_line_num))
+ return -1;
+ } else {
+ name = JS_DupAtom(s->ctx, JS_ATOM_async);
+ goto do_get_var;
+ }
+ } else {
+ if (s->token.u.ident.atom == JS_ATOM_arguments &&
+ !s->cur_func->arguments_allowed) {
+ js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");
+ return -1;
+ }
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s)) /* update line number before emitting code */
+ return -1;
+ do_get_var:
+ emit_op(s, OP_scope_get_var);
+ emit_u32(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+ }
+ break;
+ case '{':
+ case '[':
+ {
+ int skip_bits;
+ if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
+ if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
+ return -1;
+ } else {
+ if (s->token.val == '{') {
+ if (js_parse_object_literal(s))
+ return -1;
+ } else {
+ if (js_parse_array_literal(s))
+ return -1;
+ }
+ }
+ }
+ break;
+ case TOK_NEW:
+ if (next_token(s))
+ return -1;
+ if (s->token.val == '.') {
+ if (next_token(s))
+ return -1;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_target))
+ return js_parse_error(s, "expecting target");
+ if (!s->cur_func->new_target_allowed)
+ return js_parse_error(s, "new.target only allowed within functions");
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_new_target);
+ emit_u16(s, 0);
+ } else {
+ if (js_parse_postfix_expr(s, 0))
+ return -1;
+ accept_lparen = TRUE;
+ if (s->token.val != '(') {
+ /* new operator on an object */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_call_constructor);
+ emit_u16(s, 0);
+ } else {
+ call_type = FUNC_CALL_NEW;
+ }
+ }
+ break;
+ case TOK_SUPER:
+ if (next_token(s))
+ return -1;
+ if (s->token.val == '(') {
+ if (!s->cur_func->super_call_allowed)
+ return js_parse_error(s, "super() is only valid in a derived class constructor");
+ call_type = FUNC_CALL_SUPER_CTOR;
+ } else if (s->token.val == '.' || s->token.val == '[') {
+ if (!s->cur_func->super_allowed)
+ return js_parse_error(s, "'super' is only valid in a method");
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_home_object);
+ emit_u16(s, 0);
+ emit_op(s, OP_get_super);
+ } else {
+ return js_parse_error(s, "invalid use of 'super'");
+ }
+ break;
+ case TOK_IMPORT:
+ if (next_token(s))
+ return -1;
+ if (s->token.val == '.') {
+ if (next_token(s))
+ return -1;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_meta))
+ return js_parse_error(s, "meta expected");
+ if (!s->is_module)
+ return js_parse_error(s, "import.meta only valid in module code");
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_special_object);
+ emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);
+ } else {
+ if (js_parse_expect(s, '('))
+ return -1;
+ if (!accept_lparen)
+ return js_parse_error(s, "invalid use of 'import()'");
+ if (js_parse_assign_expr(s))
+ return -1;
+ if (js_parse_expect(s, ')'))
+ return -1;
+ emit_op(s, OP_import);
+ }
+ break;
+ default:
+ return js_parse_error(s, "unexpected token in expression: '%.*s'",
+ (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
+ }
+
+ optional_chaining_label = -1;
+ for(;;) {
+ JSFunctionDef *fd = s->cur_func;
+ BOOL has_optional_chain = FALSE;
+
+ if (s->token.val == TOK_QUESTION_MARK_DOT) {
+ /* optional chaining */
+ if (next_token(s))
+ return -1;
+ has_optional_chain = TRUE;
+ if (s->token.val == '(' && accept_lparen) {
+ goto parse_func_call;
+ } else if (s->token.val == '[') {
+ goto parse_array_access;
+ } else {
+ goto parse_property;
+ }
+ } else if (s->token.val == TOK_TEMPLATE &&
+ call_type == FUNC_CALL_NORMAL) {
+ if (optional_chaining_label >= 0) {
+ return js_parse_error(s, "template literal cannot appear in an optional chain");
+ }
+ call_type = FUNC_CALL_TEMPLATE;
+ goto parse_func_call2;
+ } else if (s->token.val == '(' && accept_lparen) {
+ int opcode, arg_count, drop_count;
+
+ /* function call */
+ parse_func_call:
+ if (next_token(s))
+ return -1;
+
+ if (call_type == FUNC_CALL_NORMAL) {
+ parse_func_call2:
+ switch(opcode = get_prev_opcode(fd)) {
+ case OP_get_field:
+ /* keep the object on the stack */
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
+ drop_count = 2;
+ break;
+ case OP_scope_get_private_field:
+ /* keep the object on the stack */
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
+ drop_count = 2;
+ break;
+ case OP_get_array_el:
+ /* keep the object on the stack */
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
+ drop_count = 2;
+ break;
+ case OP_scope_get_var:
+ {
+ JSAtom name;
+ int scope;
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
+ if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {
+ /* direct 'eval' */
+ opcode = OP_eval;
+ } else {
+ /* verify if function name resolves to a simple
+ get_loc/get_arg: a function call inside a `with`
+ statement can resolve to a method call of the
+ `with` context object
+ */
+ /* XXX: always generate the OP_scope_get_ref
+ and remove it in variable resolution
+ pass ? */
+ if (has_with_scope(fd, scope)) {
+ opcode = OP_scope_get_ref;
+ fd->byte_code.buf[fd->last_opcode_pos] = opcode;
+ }
+ }
+ drop_count = 1;
+ }
+ break;
+ case OP_get_super_value:
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;
+ /* on stack: this func_obj */
+ opcode = OP_get_array_el;
+ drop_count = 2;
+ break;
+ default:
+ opcode = OP_invalid;
+ drop_count = 1;
+ break;
+ }
+ if (has_optional_chain) {
+ optional_chain_test(s, &optional_chaining_label,
+ drop_count);
+ }
+ } else {
+ opcode = OP_invalid;
+ }
+
+ if (call_type == FUNC_CALL_TEMPLATE) {
+ if (js_parse_template(s, 1, &arg_count))
+ return -1;
+ goto emit_func_call;
+ } else if (call_type == FUNC_CALL_SUPER_CTOR) {
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this_active_func);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_get_super);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_new_target);
+ emit_u16(s, 0);
+ } else if (call_type == FUNC_CALL_NEW) {
+ emit_op(s, OP_dup); /* new.target = function */
+ }
+
+ /* parse arguments */
+ arg_count = 0;
+ while (s->token.val != ')') {
+ if (arg_count >= 65535) {
+ return js_parse_error(s, "Too many call arguments");
+ }
+ if (s->token.val == TOK_ELLIPSIS)
+ break;
+ if (js_parse_assign_expr(s))
+ return -1;
+ arg_count++;
+ if (s->token.val == ')')
+ break;
+ /* accept a trailing comma before the ')' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ }
+ if (s->token.val == TOK_ELLIPSIS) {
+ emit_op(s, OP_array_from);
+ emit_u16(s, arg_count);
+ emit_op(s, OP_push_i32);
+ emit_u32(s, arg_count);
+
+ /* on stack: array idx */
+ while (s->token.val != ')') {
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (next_token(s))
+ return -1;
+ if (js_parse_assign_expr(s))
+ return -1;
+#if 1
+ /* XXX: could pass is_last indicator? */
+ emit_op(s, OP_append);
+#else
+ int label_next, label_done;
+ label_next = new_label(s);
+ label_done = new_label(s);
+ /* push enumerate object below array/idx pair */
+ emit_op(s, OP_for_of_start);
+ emit_op(s, OP_rot5l);
+ emit_op(s, OP_rot5l);
+ emit_label(s, label_next);
+ /* on stack: enum_rec array idx */
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 2);
+ emit_goto(s, OP_if_true, label_done);
+ /* append element */
+ /* enum_rec array idx val -> enum_rec array new_idx */
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_inc);
+ emit_goto(s, OP_goto, label_next);
+ emit_label(s, label_done);
+ /* close enumeration, drop enum_rec and idx */
+ emit_op(s, OP_drop); /* drop undef */
+ emit_op(s, OP_nip1); /* drop enum_rec */
+ emit_op(s, OP_nip1);
+ emit_op(s, OP_nip1);
+#endif
+ } else {
+ if (js_parse_assign_expr(s))
+ return -1;
+ /* array idx val */
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_inc);
+ }
+ if (s->token.val == ')')
+ break;
+ /* accept a trailing comma before the ')' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ }
+ if (next_token(s))
+ return -1;
+ /* drop the index */
+ emit_op(s, OP_drop);
+
+ /* apply function call */
+ switch(opcode) {
+ case OP_get_field:
+ case OP_scope_get_private_field:
+ case OP_get_array_el:
+ case OP_scope_get_ref:
+ /* obj func array -> func obj array */
+ emit_op(s, OP_perm3);
+ emit_op(s, OP_apply);
+ emit_u16(s, call_type == FUNC_CALL_NEW);
+ break;
+ case OP_eval:
+ emit_op(s, OP_apply_eval);
+ emit_u16(s, fd->scope_level);
+ fd->has_eval_call = TRUE;
+ break;
+ default:
+ if (call_type == FUNC_CALL_SUPER_CTOR) {
+ emit_op(s, OP_apply);
+ emit_u16(s, 1);
+ /* set the 'this' value */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_class_field_init(s);
+ } else if (call_type == FUNC_CALL_NEW) {
+ /* obj func array -> func obj array */
+ emit_op(s, OP_perm3);
+ emit_op(s, OP_apply);
+ emit_u16(s, 1);
+ } else {
+ /* func array -> func undef array */
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_swap);
+ emit_op(s, OP_apply);
+ emit_u16(s, 0);
+ }
+ break;
+ }
+ } else {
+ if (next_token(s))
+ return -1;
+ emit_func_call:
+ switch(opcode) {
+ case OP_get_field:
+ case OP_scope_get_private_field:
+ case OP_get_array_el:
+ case OP_scope_get_ref:
+ emit_op(s, OP_call_method);
+ emit_u16(s, arg_count);
+ break;
+ case OP_eval:
+ emit_op(s, OP_eval);
+ emit_u16(s, arg_count);
+ emit_u16(s, fd->scope_level);
+ fd->has_eval_call = TRUE;
+ break;
+ default:
+ if (call_type == FUNC_CALL_SUPER_CTOR) {
+ emit_op(s, OP_call_constructor);
+ emit_u16(s, arg_count);
+
+ /* set the 'this' value */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_class_field_init(s);
+ } else if (call_type == FUNC_CALL_NEW) {
+ emit_op(s, OP_call_constructor);
+ emit_u16(s, arg_count);
+ } else {
+ emit_op(s, OP_call);
+ emit_u16(s, arg_count);
+ }
+ break;
+ }
+ }
+ call_type = FUNC_CALL_NORMAL;
+ } else if (s->token.val == '.') {
+ if (next_token(s))
+ return -1;
+ parse_property:
+ if (s->token.val == TOK_PRIVATE_NAME) {
+ /* private class field */
+ if (get_prev_opcode(fd) == OP_get_super) {
+ return js_parse_error(s, "private class field forbidden after super");
+ }
+ if (has_optional_chain) {
+ optional_chain_test(s, &optional_chaining_label, 1);
+ }
+ emit_op(s, OP_scope_get_private_field);
+ emit_atom(s, s->token.u.ident.atom);
+ emit_u16(s, s->cur_func->scope_level);
+ } else {
+ if (!token_is_ident(s->token.val)) {
+ return js_parse_error(s, "expecting field name");
+ }
+ if (get_prev_opcode(fd) == OP_get_super) {
+ JSValue val;
+ int ret;
+ val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);
+ ret = emit_push_const(s, val, 1);
+ JS_FreeValue(s->ctx, val);
+ if (ret)
+ return -1;
+ emit_op(s, OP_get_super_value);
+ } else {
+ if (has_optional_chain) {
+ optional_chain_test(s, &optional_chaining_label, 1);
+ }
+ emit_op(s, OP_get_field);
+ emit_atom(s, s->token.u.ident.atom);
+ }
+ }
+ if (next_token(s))
+ return -1;
+ } else if (s->token.val == '[') {
+ int prev_op;
+
+ parse_array_access:
+ prev_op = get_prev_opcode(fd);
+ if (has_optional_chain) {
+ optional_chain_test(s, &optional_chaining_label, 1);
+ }
+ if (next_token(s))
+ return -1;
+ if (js_parse_expr(s))
+ return -1;
+ if (js_parse_expect(s, ']'))
+ return -1;
+ if (prev_op == OP_get_super) {
+ emit_op(s, OP_get_super_value);
+ } else {
+ emit_op(s, OP_get_array_el);
+ }
+ } else {
+ break;
+ }
+ }
+ if (optional_chaining_label >= 0)
+ emit_label(s, optional_chaining_label);
+ return 0;
+}
+
+static warn_unused int js_parse_delete(JSParseState *s)
+{
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom name;
+
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+ switch (get_prev_opcode(fd)) {
+ case OP_get_field:
+ {
+ JSValue val;
+ int ret;
+
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+ val = JS_AtomToValue(s->ctx, name);
+ ret = emit_push_const(s, val, 1);
+ JS_FreeValue(s->ctx, val);
+ JS_FreeAtom(s->ctx, name);
+ if (ret)
+ return ret;
+ }
+ goto do_delete;
+ case OP_get_array_el:
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+ do_delete:
+ emit_op(s, OP_delete);
+ break;
+ case OP_scope_get_var:
+ /* 'delete this': this is not a reference */
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ if (name == JS_ATOM_this || name == JS_ATOM_new_target)
+ goto ret_true;
+ if (fd->js_mode & JS_MODE_STRICT) {
+ return js_parse_error(s, "cannot delete a direct reference in strict mode");
+ } else {
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;
+ }
+ break;
+ case OP_scope_get_private_field:
+ return js_parse_error(s, "cannot delete a private class field");
+ case OP_get_super_value:
+ emit_op(s, OP_throw_error);
+ emit_atom(s, JS_ATOM_NULL);
+ emit_u8(s, JS_THROW_ERROR_DELETE_SUPER);
+ break;
+ default:
+ ret_true:
+ emit_op(s, OP_drop);
+ emit_op(s, OP_push_true);
+ break;
+ }
+ return 0;
+}
+
+/* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */
+static warn_unused int js_parse_unary(JSParseState *s, int parse_flags)
+{
+ int op;
+
+ switch(s->token.val) {
+ case '+':
+ case '-':
+ case '!':
+ case '~':
+ case TOK_VOID:
+ op = s->token.val;
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+ switch(op) {
+ case '-':
+ emit_op(s, OP_neg);
+ break;
+ case '+':
+ emit_op(s, OP_plus);
+ break;
+ case '!':
+ emit_op(s, OP_lnot);
+ break;
+ case '~':
+ emit_op(s, OP_not);
+ break;
+ case TOK_VOID:
+ emit_op(s, OP_drop);
+ emit_op(s, OP_undefined);
+ break;
+ default:
+ abort();
+ }
+ parse_flags = 0;
+ break;
+ case TOK_DEC:
+ case TOK_INC:
+ {
+ int opcode, op, scope, label;
+ JSAtom name;
+ op = s->token.val;
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, 0))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
+ return -1;
+ emit_op(s, OP_dec + op - TOK_DEC);
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,
+ FALSE);
+ }
+ break;
+ case TOK_TYPEOF:
+ {
+ JSFunctionDef *fd;
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+ /* reference access should not return an exception, so we
+ patch the get_var */
+ fd = s->cur_func;
+ if (get_prev_opcode(fd) == OP_scope_get_var) {
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;
+ }
+ emit_op(s, OP_typeof);
+ parse_flags = 0;
+ }
+ break;
+ case TOK_DELETE:
+ if (js_parse_delete(s))
+ return -1;
+ parse_flags = 0;
+ break;
+ case TOK_AWAIT:
+ if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))
+ return js_parse_error(s, "unexpected 'await' keyword");
+ if (!s->cur_func->in_function_body)
+ return js_parse_error(s, "await in default expression");
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+ emit_op(s, OP_await);
+ parse_flags = 0;
+ break;
+ default:
+ if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) |
+ PF_POSTFIX_CALL))
+ return -1;
+ if (!s->got_lf &&
+ (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {
+ int opcode, op, scope, label;
+ JSAtom name;
+ op = s->token.val;
+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
+ return -1;
+ emit_op(s, OP_post_dec + op - TOK_DEC);
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,
+ FALSE);
+ if (next_token(s))
+ return -1;
+ }
+ break;
+ }
+ if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
+#ifdef CONFIG_BIGNUM
+ if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {
+ /* Extended exponentiation syntax rules: we extend the ES7
+ grammar in order to have more intuitive semantics:
+ -2**2 evaluates to -4. */
+ if (!(s->cur_func->js_mode & JS_MODE_MATH)) {
+ if (parse_flags & PF_POW_FORBIDDEN) {
+ JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
+ return -1;
+ }
+ }
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_ALLOWED))
+ return -1;
+ emit_op(s, OP_pow);
+ }
+#else
+ if (s->token.val == TOK_POW) {
+ /* Strict ES7 exponentiation syntax rules: To solve
+ conficting semantics between different implementations
+ regarding the precedence of prefix operators and the
+ postifx exponential, ES7 specifies that -2**2 is a
+ syntax error. */
+ if (parse_flags & PF_POW_FORBIDDEN) {
+ JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
+ return -1;
+ }
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_ALLOWED))
+ return -1;
+ emit_op(s, OP_pow);
+ }
+#endif
+ }
+ return 0;
+}
+
+/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+static warn_unused int js_parse_expr_binary(JSParseState *s, int level,
+ int parse_flags)
+{
+ int op, opcode;
+
+ if (level == 0) {
+ return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) |
+ PF_POW_ALLOWED);
+ }
+ if (js_parse_expr_binary(s, level - 1, parse_flags))
+ return -1;
+ for(;;) {
+ op = s->token.val;
+ switch(level) {
+ case 1:
+ switch(op) {
+ case '*':
+ opcode = OP_mul;
+ break;
+ case '/':
+ opcode = OP_div;
+ break;
+ case '%':
+#ifdef CONFIG_BIGNUM
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ opcode = OP_math_mod;
+ else
+#endif
+ opcode = OP_mod;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 2:
+ switch(op) {
+ case '+':
+ opcode = OP_add;
+ break;
+ case '-':
+ opcode = OP_sub;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 3:
+ switch(op) {
+ case TOK_SHL:
+ opcode = OP_shl;
+ break;
+ case TOK_SAR:
+ opcode = OP_sar;
+ break;
+ case TOK_SHR:
+ opcode = OP_shr;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 4:
+ switch(op) {
+ case '<':
+ opcode = OP_lt;
+ break;
+ case '>':
+ opcode = OP_gt;
+ break;
+ case TOK_LTE:
+ opcode = OP_lte;
+ break;
+ case TOK_GTE:
+ opcode = OP_gte;
+ break;
+ case TOK_INSTANCEOF:
+ opcode = OP_instanceof;
+ break;
+ case TOK_IN:
+ if (parse_flags & PF_IN_ACCEPTED) {
+ opcode = OP_in;
+ } else {
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 5:
+ switch(op) {
+ case TOK_EQ:
+ opcode = OP_eq;
+ break;
+ case TOK_NEQ:
+ opcode = OP_neq;
+ break;
+ case TOK_STRICT_EQ:
+ opcode = OP_strict_eq;
+ break;
+ case TOK_STRICT_NEQ:
+ opcode = OP_strict_neq;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 6:
+ switch(op) {
+ case '&':
+ opcode = OP_and;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 7:
+ switch(op) {
+ case '^':
+ opcode = OP_xor;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 8:
+ switch(op) {
+ case '|':
+ opcode = OP_or;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ default:
+ abort();
+ }
+ if (next_token(s))
+ return -1;
+ if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC))
+ return -1;
+ emit_op(s, opcode);
+ }
+ return 0;
+}
+
+/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+static warn_unused int js_parse_logical_and_or(JSParseState *s, int op,
+ int parse_flags)
+{
+ int label1;
+
+ if (op == TOK_LAND) {
+ if (js_parse_expr_binary(s, 8, parse_flags))
+ return -1;
+ } else {
+ if (js_parse_logical_and_or(s, TOK_LAND, parse_flags))
+ return -1;
+ }
+ if (s->token.val == op) {
+ label1 = new_label(s);
+
+ for(;;) {
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_dup);
+ emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);
+ emit_op(s, OP_drop);
+
+ if (op == TOK_LAND) {
+ if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
+ return -1;
+ } else {
+ if (js_parse_logical_and_or(s, TOK_LAND,
+ parse_flags & ~PF_ARROW_FUNC))
+ return -1;
+ }
+ if (s->token.val != op) {
+ if (s->token.val == TOK_DOUBLE_QUESTION_MARK)
+ return js_parse_error(s, "cannot mix ?? with && or ||");
+ break;
+ }
+ }
+
+ emit_label(s, label1);
+ }
+ return 0;
+}
+
+static warn_unused int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
+{
+ int label1;
+
+ if (js_parse_logical_and_or(s, TOK_LOR, parse_flags))
+ return -1;
+ if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {
+ label1 = new_label(s);
+ for(;;) {
+ if (next_token(s))
+ return -1;
+
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined_or_null);
+ emit_goto(s, OP_if_false, label1);
+ emit_op(s, OP_drop);
+
+ if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
+ return -1;
+ if (s->token.val != TOK_DOUBLE_QUESTION_MARK)
+ break;
+ }
+ emit_label(s, label1);
+ }
+ return 0;
+}
+
+/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+static warn_unused int js_parse_cond_expr(JSParseState *s, int parse_flags)
+{
+ int label1, label2;
+
+ if (js_parse_coalesce_expr(s, parse_flags))
+ return -1;
+ if (s->token.val == '?') {
+ if (next_token(s))
+ return -1;
+ label1 = emit_goto(s, OP_if_false, -1);
+
+ if (js_parse_assign_expr(s))
+ return -1;
+ if (js_parse_expect(s, ':'))
+ return -1;
+
+ label2 = emit_goto(s, OP_goto, -1);
+
+ emit_label(s, label1);
+
+ if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED))
+ return -1;
+
+ emit_label(s, label2);
+ }
+ return 0;
+}
+
+static void emit_return(JSParseState *s, BOOL hasval);
+
+/* allowed parse_flags: PF_IN_ACCEPTED */
+static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags)
+{
+ int opcode, op, scope;
+ JSAtom name0 = JS_ATOM_NULL;
+ JSAtom name;
+
+ if (s->token.val == TOK_YIELD) {
+ BOOL is_star = FALSE, is_async;
+
+ if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))
+ return js_parse_error(s, "unexpected 'yield' keyword");
+ if (!s->cur_func->in_function_body)
+ return js_parse_error(s, "yield in default expression");
+ if (next_token(s))
+ return -1;
+ /* XXX: is there a better method to detect 'yield' without
+ parameters ? */
+ if (s->token.val != ';' && s->token.val != ')' &&
+ s->token.val != ']' && s->token.val != '}' &&
+ s->token.val != ',' && s->token.val != ':' && !s->got_lf) {
+ if (s->token.val == '*') {
+ is_star = TRUE;
+ if (next_token(s))
+ return -1;
+ }
+ if (js_parse_assign_expr2(s, parse_flags))
+ return -1;
+ } else {
+ emit_op(s, OP_undefined);
+ }
+ is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR);
+
+ if (is_star) {
+ int label_loop, label_return, label_next;
+ int label_return1, label_yield, label_throw, label_throw1;
+ int label_throw2;
+
+ label_loop = new_label(s);
+ label_yield = new_label(s);
+
+ emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);
+
+ /* remove the catch offset (XXX: could avoid pushing back
+ undefined) */
+ emit_op(s, OP_drop);
+ emit_op(s, OP_undefined);
+
+ emit_op(s, OP_undefined); /* initial value */
+
+ emit_label(s, label_loop);
+ emit_op(s, OP_iterator_next);
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_done);
+ label_next = emit_goto(s, OP_if_true, -1); /* end of loop */
+ emit_label(s, label_yield);
+ if (is_async) {
+ /* OP_async_yield_star takes the value as parameter */
+ emit_op(s, OP_get_field);
+ emit_atom(s, JS_ATOM_value);
+ emit_op(s, OP_await);
+ emit_op(s, OP_async_yield_star);
+ } else {
+ /* OP_yield_star takes (value, done) as parameter */
+ emit_op(s, OP_yield_star);
+ }
+ emit_op(s, OP_dup);
+ label_return = emit_goto(s, OP_if_true, -1);
+ emit_op(s, OP_drop);
+ emit_goto(s, OP_goto, label_loop);
+
+ emit_label(s, label_return);
+ emit_op(s, OP_push_i32);
+ emit_u32(s, 2);
+ emit_op(s, OP_strict_eq);
+ label_throw = emit_goto(s, OP_if_true, -1);
+
+ /* return handling */
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_iterator_call);
+ emit_u8(s, 0);
+ label_return1 = emit_goto(s, OP_if_true, -1);
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_done);
+ emit_goto(s, OP_if_false, label_yield);
+
+ emit_op(s, OP_get_field);
+ emit_atom(s, JS_ATOM_value);
+
+ emit_label(s, label_return1);
+ emit_op(s, OP_nip);
+ emit_op(s, OP_nip);
+ emit_op(s, OP_nip);
+ emit_return(s, TRUE);
+
+ /* throw handling */
+ emit_label(s, label_throw);
+ emit_op(s, OP_iterator_call);
+ emit_u8(s, 1);
+ label_throw1 = emit_goto(s, OP_if_true, -1);
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_done);
+ emit_goto(s, OP_if_false, label_yield);
+ emit_goto(s, OP_goto, label_next);
+ /* close the iterator and throw a type error exception */
+ emit_label(s, label_throw1);
+ emit_op(s, OP_iterator_call);
+ emit_u8(s, 2);
+ label_throw2 = emit_goto(s, OP_if_true, -1);
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_label(s, label_throw2);
+
+ emit_op(s, OP_throw_error);
+ emit_atom(s, JS_ATOM_NULL);
+ emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW);
+
+ emit_label(s, label_next);
+ emit_op(s, OP_get_field);
+ emit_atom(s, JS_ATOM_value);
+ emit_op(s, OP_nip); /* keep the value associated with
+ done = true */
+ emit_op(s, OP_nip);
+ emit_op(s, OP_nip);
+ } else {
+ int label_next;
+
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_yield);
+ label_next = emit_goto(s, OP_if_false, -1);
+ emit_return(s, TRUE);
+ emit_label(s, label_next);
+ }
+ return 0;
+ }
+ if (s->token.val == TOK_IDENT) {
+ /* name0 is used to check for OP_set_name pattern, not duplicated */
+ name0 = s->token.u.ident.atom;
+ }
+ if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC))
+ return -1;
+
+ op = s->token.val;
+ if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {
+ int label;
+ if (next_token(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)
+ return -1;
+
+ if (js_parse_assign_expr2(s, parse_flags)) {
+ JS_FreeAtom(s->ctx, name);
+ return -1;
+ }
+
+ if (op == '=') {
+ if (opcode == OP_get_ref_value && name == name0) {
+ set_object_name(s, name);
+ }
+ } else {
+ static const uint8_t assign_opcodes[] = {
+ OP_mul, OP_div, OP_mod, OP_add, OP_sub,
+ OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
+#ifdef CONFIG_BIGNUM
+ OP_pow,
+#endif
+ OP_pow,
+ };
+ op = assign_opcodes[op - TOK_MUL_ASSIGN];
+#ifdef CONFIG_BIGNUM
+ if (s->cur_func->js_mode & JS_MODE_MATH) {
+ if (op == OP_mod)
+ op = OP_math_mod;
+ }
+#endif
+ emit_op(s, op);
+ }
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
+ } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
+ int label, label1, depth_lvalue, label2;
+
+ if (next_token(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &name, &label,
+ &depth_lvalue, TRUE, op) < 0)
+ return -1;
+
+ emit_op(s, OP_dup);
+ if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)
+ emit_op(s, OP_is_undefined_or_null);
+ label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,
+ -1);
+ emit_op(s, OP_drop);
+
+ if (js_parse_assign_expr2(s, parse_flags)) {
+ JS_FreeAtom(s->ctx, name);
+ return -1;
+ }
+
+ if (opcode == OP_get_ref_value && name == name0) {
+ set_object_name(s, name);
+ }
+
+ switch(depth_lvalue) {
+ case 1:
+ emit_op(s, OP_insert2);
+ break;
+ case 2:
+ emit_op(s, OP_insert3);
+ break;
+ case 3:
+ emit_op(s, OP_insert4);
+ break;
+ default:
+ abort();
+ }
+
+ /* XXX: we disable the OP_put_ref_value optimization by not
+ using put_lvalue() otherwise depth_lvalue is not correct */
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,
+ FALSE);
+ label2 = emit_goto(s, OP_goto, -1);
+
+ emit_label(s, label1);
+
+ /* remove the lvalue stack entries */
+ while (depth_lvalue != 0) {
+ emit_op(s, OP_nip);
+ depth_lvalue--;
+ }
+
+ emit_label(s, label2);
+ }
+ return 0;
+}
+
+static warn_unused int js_parse_assign_expr(JSParseState *s)
+{
+ return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
+}
+
+/* allowed parse_flags: PF_IN_ACCEPTED */
+static warn_unused int js_parse_expr2(JSParseState *s, int parse_flags)
+{
+ BOOL comma = FALSE;
+ for(;;) {
+ if (js_parse_assign_expr2(s, parse_flags))
+ return -1;
+ if (comma) {
+ /* prevent get_lvalue from using the last expression
+ as an lvalue. This also prevents the conversion of
+ of get_var to get_ref for method lookup in function
+ call inside `with` statement.
+ */
+ s->cur_func->last_opcode_pos = -1;
+ }
+ if (s->token.val != ',')
+ break;
+ comma = TRUE;
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_drop);
+ }
+ return 0;
+}
+
+static warn_unused int js_parse_expr(JSParseState *s)
+{
+ return js_parse_expr2(s, PF_IN_ACCEPTED);
+}
+
+static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
+ JSAtom label_name,
+ int label_break, int label_cont,
+ int drop_count)
+{
+ be->prev = fd->top_break;
+ fd->top_break = be;
+ be->label_name = label_name;
+ be->label_break = label_break;
+ be->label_cont = label_cont;
+ be->drop_count = drop_count;
+ be->label_finally = -1;
+ be->scope_level = fd->scope_level;
+ be->has_iterator = FALSE;
+}
+
+static void pop_break_entry(JSFunctionDef *fd)
+{
+ BlockEnv *be;
+ be = fd->top_break;
+ fd->top_break = be->prev;
+}
+
+static warn_unused int emit_break(JSParseState *s, JSAtom name, int is_cont)
+{
+ BlockEnv *top;
+ int i, scope_level;
+
+ scope_level = s->cur_func->scope_level;
+ top = s->cur_func->top_break;
+ while (top != NULL) {
+ close_scopes(s, scope_level, top->scope_level);
+ scope_level = top->scope_level;
+ if (is_cont &&
+ top->label_cont != -1 &&
+ (name == JS_ATOM_NULL || top->label_name == name)) {
+ /* continue stays inside the same block */
+ emit_goto(s, OP_goto, top->label_cont);
+ return 0;
+ }
+ if (!is_cont &&
+ top->label_break != -1 &&
+ (name == JS_ATOM_NULL || top->label_name == name)) {
+ emit_goto(s, OP_goto, top->label_break);
+ return 0;
+ }
+ i = 0;
+ if (top->has_iterator) {
+ emit_op(s, OP_iterator_close);
+ i += 3;
+ }
+ for(; i < top->drop_count; i++)
+ emit_op(s, OP_drop);
+ if (top->label_finally != -1) {
+ /* must push dummy value to keep same stack depth */
+ emit_op(s, OP_undefined);
+ emit_goto(s, OP_gosub, top->label_finally);
+ emit_op(s, OP_drop);
+ }
+ top = top->prev;
+ }
+ if (name == JS_ATOM_NULL) {
+ if (is_cont)
+ return js_parse_error(s, "continue must be inside loop");
+ else
+ return js_parse_error(s, "break must be inside loop or switch");
+ } else {
+ return js_parse_error(s, "break/continue label not found");
+ }
+}
+
+/* execute the finally blocks before return */
+static void emit_return(JSParseState *s, BOOL hasval)
+{
+ BlockEnv *top;
+ int drop_count;
+
+ drop_count = 0;
+ top = s->cur_func->top_break;
+ while (top != NULL) {
+ /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not
+ required as all local variables will be closed upon returning
+ from JS_CallInternal, but not in the same order. */
+ if (top->has_iterator) {
+ /* with 'yield', the exact number of OP_drop to emit is
+ unknown, so we use a specific operation to look for
+ the catch offset */
+ if (!hasval) {
+ emit_op(s, OP_undefined);
+ hasval = TRUE;
+ }
+ emit_op(s, OP_iterator_close_return);
+ if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ int label_next, label_next2;
+
+ emit_op(s, OP_drop); /* catch offset */
+ emit_op(s, OP_drop); /* next */
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_return);
+ /* stack: iter_obj return_func */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined_or_null);
+ label_next = emit_goto(s, OP_if_true, -1);
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_await);
+ label_next2 = emit_goto(s, OP_goto, -1);
+ emit_label(s, label_next);
+ emit_op(s, OP_drop);
+ emit_label(s, label_next2);
+ emit_op(s, OP_drop);
+ } else {
+ emit_op(s, OP_iterator_close);
+ }
+ drop_count = -3;
+ }
+ drop_count += top->drop_count;
+ if (top->label_finally != -1) {
+ while(drop_count) {
+ /* must keep the stack top if hasval */
+ emit_op(s, hasval ? OP_nip : OP_drop);
+ drop_count--;
+ }
+ if (!hasval) {
+ /* must push return value to keep same stack size */
+ emit_op(s, OP_undefined);
+ hasval = TRUE;
+ }
+ emit_goto(s, OP_gosub, top->label_finally);
+ }
+ top = top->prev;
+ }
+ if (s->cur_func->is_derived_class_constructor) {
+ int label_return;
+
+ /* 'this' can be uninitialized, so it may be accessed only if
+ the derived class constructor does not return an object */
+ if (hasval) {
+ emit_op(s, OP_check_ctor_return);
+ label_return = emit_goto(s, OP_if_false, -1);
+ emit_op(s, OP_drop);
+ } else {
+ label_return = -1;
+ }
+
+ /* XXX: if this is not initialized, should throw the
+ ReferenceError in the caller realm */
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_label(s, label_return);
+ emit_op(s, OP_return);
+ } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
+ if (!hasval) {
+ emit_op(s, OP_undefined);
+ } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ emit_op(s, OP_await);
+ }
+ emit_op(s, OP_return_async);
+ } else {
+ emit_op(s, hasval ? OP_return : OP_return_undef);
+ }
+}
+
+#define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */
+/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */
+#define DECL_MASK_FUNC_WITH_LABEL (1 << 1)
+#define DECL_MASK_OTHER (1 << 2) /* all other declarations */
+#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
+
+static warn_unused int js_parse_statement_or_decl(JSParseState *s,
+ int decl_mask);
+
+static warn_unused int js_parse_statement(JSParseState *s)
+{
+ return js_parse_statement_or_decl(s, 0);
+}
+
+static warn_unused int js_parse_block(JSParseState *s)
+{
+ if (js_parse_expect(s, '{'))
+ return -1;
+ if (s->token.val != '}') {
+ push_scope(s);
+ for(;;) {
+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
+ return -1;
+ if (s->token.val == '}')
+ break;
+ }
+ pop_scope(s);
+ }
+ if (next_token(s))
+ return -1;
+ return 0;
+}
+
+/* allowed parse_flags: PF_IN_ACCEPTED */
+static warn_unused int js_parse_var(JSParseState *s, int parse_flags, int tok,
+ BOOL export_flag)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom name = JS_ATOM_NULL;
+
+ for (;;) {
+ if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved) {
+ return js_parse_error_reserved_identifier(s);
+ }
+ name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {
+ js_parse_error(s, "'let' is not a valid lexical identifier");
+ goto var_error;
+ }
+ if (next_token(s))
+ goto var_error;
+ if (js_define_var(s, name, tok))
+ goto var_error;
+ if (export_flag) {
+ if (!add_export_entry(s, s->cur_func->module, name, name,
+ JS_EXPORT_TYPE_LOCAL))
+ goto var_error;
+ }
+
+ if (s->token.val == '=') {
+ if (next_token(s))
+ goto var_error;
+ if (tok == TOK_VAR) {
+ /* Must make a reference for proper `with` semantics */
+ int opcode, scope, label;
+ JSAtom name1;
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)
+ goto var_error;
+ if (js_parse_assign_expr2(s, parse_flags)) {
+ JS_FreeAtom(ctx, name1);
+ goto var_error;
+ }
+ set_object_name(s, name);
+ put_lvalue(s, opcode, scope, name1, label,
+ PUT_LVALUE_NOKEEP, FALSE);
+ } else {
+ if (js_parse_assign_expr2(s, parse_flags))
+ goto var_error;
+ set_object_name(s, name);
+ emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
+ OP_scope_put_var_init : OP_scope_put_var);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ }
+ } else {
+ if (tok == TOK_CONST) {
+ js_parse_error(s, "missing initializer for const variable");
+ goto var_error;
+ }
+ if (tok == TOK_LET) {
+ /* initialize lexical variable upon entering its scope */
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ }
+ }
+ JS_FreeAtom(ctx, name);
+ } else {
+ int skip_bits;
+ if ((s->token.val == '[' || s->token.val == '{')
+ && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
+ emit_op(s, OP_undefined);
+ if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
+ return -1;
+ } else {
+ return js_parse_error(s, "variable name expected");
+ }
+ }
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ return -1;
+ }
+ return 0;
+
+ var_error:
+ JS_FreeAtom(ctx, name);
+ return -1;
+}
+
+/* test if the current token is a label. Use simplistic look-ahead scanner */
+static BOOL is_label(JSParseState *s)
+{
+ return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
+ peek_token(s, FALSE) == ':');
+}
+
+/* test if the current token is a let keyword. Use simplistic look-ahead scanner */
+static int is_let(JSParseState *s, int decl_mask)
+{
+ int res = FALSE;
+
+ if (token_is_pseudo_keyword(s, JS_ATOM_let)) {
+#if 1
+ JSParsePos pos;
+ js_parse_get_pos(s, &pos);
+ for (;;) {
+ if (next_token(s)) {
+ res = -1;
+ break;
+ }
+ if (s->token.val == '[') {
+ /* let [ is a syntax restriction:
+ it never introduces an ExpressionStatement */
+ res = TRUE;
+ break;
+ }
+ if (s->token.val == '{' ||
+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||
+ s->token.val == TOK_LET ||
+ s->token.val == TOK_YIELD ||
+ s->token.val == TOK_AWAIT) {
+ /* Check for possible ASI if not scanning for Declaration */
+ /* XXX: should also check that `{` introduces a BindingPattern,
+ but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */
+ if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) {
+ res = TRUE;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ if (js_parse_seek_token(s, &pos)) {
+ res = -1;
+ }
+#else
+ int tok = peek_token(s, TRUE);
+ if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') {
+ res = TRUE;
+ }
+#endif
+ }
+ return res;
+}
+
+/* XXX: handle IteratorClose when exiting the loop before the
+ enumeration is done */
+static warn_unused int js_parse_for_in_of(JSParseState *s, int label_name,
+ BOOL is_async)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom var_name;
+ BOOL has_initializer, is_for_of, has_destructuring;
+ int tok, tok1, opcode, scope, block_scope_level;
+ int label_next, label_expr, label_cont, label_body, label_break;
+ int pos_next, pos_expr;
+ BlockEnv break_entry;
+
+ has_initializer = FALSE;
+ has_destructuring = FALSE;
+ is_for_of = FALSE;
+ block_scope_level = fd->scope_level;
+ label_cont = new_label(s);
+ label_body = new_label(s);
+ label_break = new_label(s);
+ label_next = new_label(s);
+
+ /* create scope for the lexical variables declared in the enumeration
+ expressions. XXX: Not completely correct because of weird capturing
+ semantics in `for (i of o) a.push(function(){return i})` */
+ push_scope(s);
+
+ /* local for_in scope starts here so individual elements
+ can be closed in statement. */
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, label_cont, 1);
+ break_entry.scope_level = block_scope_level;
+
+ label_expr = emit_goto(s, OP_goto, -1);
+
+ pos_next = s->cur_func->byte_code.size;
+ emit_label(s, label_next);
+
+ tok = s->token.val;
+ switch (is_let(s, DECL_MASK_OTHER)) {
+ case TRUE:
+ tok = TOK_LET;
+ break;
+ case FALSE:
+ break;
+ default:
+ return -1;
+ }
+ if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
+ if (next_token(s))
+ return -1;
+
+ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
+ if (s->token.val == '[' || s->token.val == '{') {
+ if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0)
+ return -1;
+ has_destructuring = TRUE;
+ } else {
+ return js_parse_error(s, "variable name expected");
+ }
+ var_name = JS_ATOM_NULL;
+ } else {
+ var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)) {
+ JS_FreeAtom(s->ctx, var_name);
+ return -1;
+ }
+ if (js_define_var(s, var_name, tok)) {
+ JS_FreeAtom(s->ctx, var_name);
+ return -1;
+ }
+ emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
+ OP_scope_put_var_init : OP_scope_put_var);
+ emit_atom(s, var_name);
+ emit_u16(s, fd->scope_level);
+ }
+ } else {
+ int skip_bits;
+ if ((s->token.val == '[' || s->token.val == '{')
+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
+ if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
+ return -1;
+ } else {
+ int lvalue_label;
+ if (js_parse_left_hand_side_expr(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
+ NULL, FALSE, TOK_FOR))
+ return -1;
+ put_lvalue(s, opcode, scope, var_name, lvalue_label,
+ PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
+ }
+ var_name = JS_ATOM_NULL;
+ }
+ emit_goto(s, OP_goto, label_body);
+
+ pos_expr = s->cur_func->byte_code.size;
+ emit_label(s, label_expr);
+ if (s->token.val == '=') {
+ /* XXX: potential scoping issue if inside `with` statement */
+ has_initializer = TRUE;
+ /* parse and evaluate initializer prior to evaluating the
+ object (only used with "for in" with a non lexical variable
+ in non strict mode */
+ if (next_token(s) || js_parse_assign_expr2(s, 0)) {
+ JS_FreeAtom(ctx, var_name);
+ return -1;
+ }
+ if (var_name != JS_ATOM_NULL) {
+ emit_op(s, OP_scope_put_var);
+ emit_atom(s, var_name);
+ emit_u16(s, fd->scope_level);
+ }
+ }
+ JS_FreeAtom(ctx, var_name);
+
+ if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
+ break_entry.has_iterator = is_for_of = TRUE;
+ break_entry.drop_count += 2;
+ if (has_initializer)
+ goto initializer_error;
+ } else if (s->token.val == TOK_IN) {
+ if (is_async)
+ return js_parse_error(s, "'for await' loop should be used with 'of'");
+ if (has_initializer &&
+ (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||
+ has_destructuring)) {
+ initializer_error:
+ return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",
+ is_for_of ? "of" : "in");
+ }
+ } else {
+ return js_parse_error(s, "expected 'of' or 'in' in for control expression");
+ }
+ if (next_token(s))
+ return -1;
+ if (is_for_of) {
+ if (js_parse_assign_expr(s))
+ return -1;
+ } else {
+ if (js_parse_expr(s))
+ return -1;
+ }
+ /* close the scope after having evaluated the expression so that
+ the TDZ values are in the closures */
+ close_scopes(s, s->cur_func->scope_level, block_scope_level);
+ if (is_for_of) {
+ if (is_async)
+ emit_op(s, OP_for_await_of_start);
+ else
+ emit_op(s, OP_for_of_start);
+ /* on stack: enum_rec */
+ } else {
+ emit_op(s, OP_for_in_start);
+ /* on stack: enum_obj */
+ }
+ emit_goto(s, OP_goto, label_cont);
+
+ if (js_parse_expect(s, ')'))
+ return -1;
+
+ if (OPTIMIZE) {
+ /* move the `next` code here */
+ DynBuf *bc = &s->cur_func->byte_code;
+ int chunk_size = pos_expr - pos_next;
+ int offset = bc->size - pos_next;
+ int i;
+ dbuf_realloc(bc, bc->size + chunk_size);
+ dbuf_put(bc, bc->buf + pos_next, chunk_size);
+ memset(bc->buf + pos_next, OP_nop, chunk_size);
+ /* `next` part ends with a goto */
+ s->cur_func->last_opcode_pos = bc->size - 5;
+ /* relocate labels */
+ for (i = label_cont; i < s->cur_func->label_count; i++) {
+ LabelSlot *ls = &s->cur_func->label_slots[i];
+ if (ls->pos >= pos_next && ls->pos < pos_expr)
+ ls->pos += offset;
+ }
+ }
+
+ emit_label(s, label_body);
+ if (js_parse_statement(s))
+ return -1;
+
+ close_scopes(s, s->cur_func->scope_level, block_scope_level);
+
+ emit_label(s, label_cont);
+ if (is_for_of) {
+ if (is_async) {
+ /* call the next method */
+ /* stack: iter_obj next catch_offset */
+ emit_op(s, OP_dup3);
+ emit_op(s, OP_drop);
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ /* get the result of the promise */
+ emit_op(s, OP_await);
+ /* unwrap the value and done values */
+ emit_op(s, OP_iterator_get_value_done);
+ } else {
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 0);
+ }
+ } else {
+ emit_op(s, OP_for_in_next);
+ }
+ /* on stack: enum_rec / enum_obj value bool */
+ emit_goto(s, OP_if_false, label_next);
+ /* drop the undefined value from for_xx_next */
+ emit_op(s, OP_drop);
+
+ emit_label(s, label_break);
+ if (is_for_of) {
+ /* close and drop enum_rec */
+ emit_op(s, OP_iterator_close);
+ } else {
+ emit_op(s, OP_drop);
+ }
+ pop_break_entry(s->cur_func);
+ pop_scope(s);
+ return 0;
+}
+
+static void set_eval_ret_undefined(JSParseState *s)
+{
+ if (s->cur_func->eval_ret_idx >= 0) {
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_put_loc);
+ emit_u16(s, s->cur_func->eval_ret_idx);
+ }
+}
+
+static warn_unused int js_parse_statement_or_decl(JSParseState *s,
+ int decl_mask)
+{
+ JSContext *ctx = s->ctx;
+ JSAtom label_name;
+ int tok;
+
+ /* specific label handling */
+ /* XXX: support multiple labels on loop statements */
+ label_name = JS_ATOM_NULL;
+ if (is_label(s)) {
+ BlockEnv *be;
+
+ label_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+
+ for (be = s->cur_func->top_break; be; be = be->prev) {
+ if (be->label_name == label_name) {
+ js_parse_error(s, "duplicate label name");
+ goto fail;
+ }
+ }
+
+ if (next_token(s))
+ goto fail;
+ if (js_parse_expect(s, ':'))
+ goto fail;
+ if (s->token.val != TOK_FOR
+ && s->token.val != TOK_DO
+ && s->token.val != TOK_WHILE) {
+ /* labelled regular statement */
+ int label_break, mask;
+ BlockEnv break_entry;
+
+ label_break = new_label(s);
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, -1, 0);
+ if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&
+ (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
+ mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
+ } else {
+ mask = 0;
+ }
+ if (js_parse_statement_or_decl(s, mask))
+ goto fail;
+ emit_label(s, label_break);
+ pop_break_entry(s->cur_func);
+ goto done;
+ }
+ }
+
+ switch(tok = s->token.val) {
+ case '{':
+ if (js_parse_block(s))
+ goto fail;
+ break;
+ case TOK_RETURN:
+ if (s->cur_func->is_eval) {
+ js_parse_error(s, "return not in a function");
+ goto fail;
+ }
+ if (next_token(s))
+ goto fail;
+ if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
+ if (js_parse_expr(s))
+ goto fail;
+ emit_return(s, TRUE);
+ } else {
+ emit_return(s, FALSE);
+ }
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+ case TOK_THROW:
+ if (next_token(s))
+ goto fail;
+ if (s->got_lf) {
+ js_parse_error(s, "line terminator not allowed after throw");
+ goto fail;
+ }
+ if (js_parse_expr(s))
+ goto fail;
+ emit_op(s, OP_throw);
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+ case TOK_LET:
+ case TOK_CONST:
+ haslet:
+ if (!(decl_mask & DECL_MASK_OTHER)) {
+ js_parse_error(s, "lexical declarations can't appear in single-statement context");
+ goto fail;
+ }
+ /* fall thru */
+ case TOK_VAR:
+ if (next_token(s))
+ goto fail;
+ if (js_parse_var(s, TRUE, tok, FALSE))
+ goto fail;
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+ case TOK_IF:
+ {
+ int label1, label2, mask;
+ if (next_token(s))
+ goto fail;
+ /* create a new scope for `let f;if(1) function f(){}` */
+ push_scope(s);
+ set_eval_ret_undefined(s);
+ if (js_parse_expr_paren(s))
+ goto fail;
+ label1 = emit_goto(s, OP_if_false, -1);
+ if (s->cur_func->js_mode & JS_MODE_STRICT)
+ mask = 0;
+ else
+ mask = DECL_MASK_FUNC; /* Annex B.3.4 */
+
+ if (js_parse_statement_or_decl(s, mask))
+ goto fail;
+
+ if (s->token.val == TOK_ELSE) {
+ label2 = emit_goto(s, OP_goto, -1);
+ if (next_token(s))
+ goto fail;
+
+ emit_label(s, label1);
+ if (js_parse_statement_or_decl(s, mask))
+ goto fail;
+
+ label1 = label2;
+ }
+ emit_label(s, label1);
+ pop_scope(s);
+ }
+ break;
+ case TOK_WHILE:
+ {
+ int label_cont, label_break;
+ BlockEnv break_entry;
+
+ label_cont = new_label(s);
+ label_break = new_label(s);
+
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, label_cont, 0);
+
+ if (next_token(s))
+ goto fail;
+
+ set_eval_ret_undefined(s);
+
+ emit_label(s, label_cont);
+ if (js_parse_expr_paren(s))
+ goto fail;
+ emit_goto(s, OP_if_false, label_break);
+
+ if (js_parse_statement(s))
+ goto fail;
+ emit_goto(s, OP_goto, label_cont);
+
+ emit_label(s, label_break);
+
+ pop_break_entry(s->cur_func);
+ }
+ break;
+ case TOK_DO:
+ {
+ int label_cont, label_break, label1;
+ BlockEnv break_entry;
+
+ label_cont = new_label(s);
+ label_break = new_label(s);
+ label1 = new_label(s);
+
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, label_cont, 0);
+
+ if (next_token(s))
+ goto fail;
+
+ emit_label(s, label1);
+
+ set_eval_ret_undefined(s);
+
+ if (js_parse_statement(s))
+ goto fail;
+
+ emit_label(s, label_cont);
+ if (js_parse_expect(s, TOK_WHILE))
+ goto fail;
+ if (js_parse_expr_paren(s))
+ goto fail;
+ /* Insert semicolon if missing */
+ if (s->token.val == ';') {
+ if (next_token(s))
+ goto fail;
+ }
+ emit_goto(s, OP_if_true, label1);
+
+ emit_label(s, label_break);
+
+ pop_break_entry(s->cur_func);
+ }
+ break;
+ case TOK_FOR:
+ {
+ int label_cont, label_break, label_body, label_test;
+ int pos_cont, pos_body, block_scope_level;
+ BlockEnv break_entry;
+ int tok, bits;
+ BOOL is_async;
+
+ if (next_token(s))
+ goto fail;
+
+ set_eval_ret_undefined(s);
+ bits = 0;
+ is_async = FALSE;
+ if (s->token.val == '(') {
+ js_parse_skip_parens_token(s, &bits, FALSE);
+ } else if (s->token.val == TOK_AWAIT) {
+ if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {
+ js_parse_error(s, "for await is only valid in asynchronous functions");
+ goto fail;
+ }
+ is_async = TRUE;
+ if (next_token(s))
+ goto fail;
+ }
+ if (js_parse_expect(s, '('))
+ goto fail;
+
+ if (!(bits & SKIP_HAS_SEMI)) {
+ /* parse for/in or for/of */
+ if (js_parse_for_in_of(s, label_name, is_async))
+ goto fail;
+ break;
+ }
+ block_scope_level = s->cur_func->scope_level;
+
+ /* create scope for the lexical variables declared in the initial,
+ test and increment expressions */
+ push_scope(s);
+ /* initial expression */
+ tok = s->token.val;
+ if (tok != ';') {
+ switch (is_let(s, DECL_MASK_OTHER)) {
+ case TRUE:
+ tok = TOK_LET;
+ break;
+ case FALSE:
+ break;
+ default:
+ goto fail;
+ }
+ if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
+ if (next_token(s))
+ goto fail;
+ if (js_parse_var(s, FALSE, tok, FALSE))
+ goto fail;
+ } else {
+ if (js_parse_expr2(s, FALSE))
+ goto fail;
+ emit_op(s, OP_drop);
+ }
+
+ /* close the closures before the first iteration */
+ close_scopes(s, s->cur_func->scope_level, block_scope_level);
+ }
+ if (js_parse_expect(s, ';'))
+ goto fail;
+
+ label_test = new_label(s);
+ label_cont = new_label(s);
+ label_body = new_label(s);
+ label_break = new_label(s);
+
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, label_cont, 0);
+
+ /* test expression */
+ if (s->token.val == ';') {
+ /* no test expression */
+ label_test = label_body;
+ } else {
+ emit_label(s, label_test);
+ if (js_parse_expr(s))
+ goto fail;
+ emit_goto(s, OP_if_false, label_break);
+ }
+ if (js_parse_expect(s, ';'))
+ goto fail;
+
+ if (s->token.val == ')') {
+ /* no end expression */
+ break_entry.label_cont = label_cont = label_test;
+ pos_cont = 0; /* avoid warning */
+ } else {
+ /* skip the end expression */
+ emit_goto(s, OP_goto, label_body);
+
+ pos_cont = s->cur_func->byte_code.size;
+ emit_label(s, label_cont);
+ if (js_parse_expr(s))
+ goto fail;
+ emit_op(s, OP_drop);
+ if (label_test != label_body)
+ emit_goto(s, OP_goto, label_test);
+ }
+ if (js_parse_expect(s, ')'))
+ goto fail;
+
+ pos_body = s->cur_func->byte_code.size;
+ emit_label(s, label_body);
+ if (js_parse_statement(s))
+ goto fail;
+
+ /* close the closures before the next iteration */
+ /* XXX: check continue case */
+ close_scopes(s, s->cur_func->scope_level, block_scope_level);
+
+ if (OPTIMIZE && label_test != label_body && label_cont != label_test) {
+ /* move the increment code here */
+ DynBuf *bc = &s->cur_func->byte_code;
+ int chunk_size = pos_body - pos_cont;
+ int offset = bc->size - pos_cont;
+ int i;
+ dbuf_realloc(bc, bc->size + chunk_size);
+ dbuf_put(bc, bc->buf + pos_cont, chunk_size);
+ memset(bc->buf + pos_cont, OP_nop, chunk_size);
+ /* increment part ends with a goto */
+ s->cur_func->last_opcode_pos = bc->size - 5;
+ /* relocate labels */
+ for (i = label_cont; i < s->cur_func->label_count; i++) {
+ LabelSlot *ls = &s->cur_func->label_slots[i];
+ if (ls->pos >= pos_cont && ls->pos < pos_body)
+ ls->pos += offset;
+ }
+ } else {
+ emit_goto(s, OP_goto, label_cont);
+ }
+
+ emit_label(s, label_break);
+
+ pop_break_entry(s->cur_func);
+ pop_scope(s);
+ }
+ break;
+ case TOK_BREAK:
+ case TOK_CONTINUE:
+ {
+ int is_cont = s->token.val - TOK_BREAK;
+ int label;
+
+ if (next_token(s))
+ goto fail;
+ if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
+ label = s->token.u.ident.atom;
+ else
+ label = JS_ATOM_NULL;
+ if (emit_break(s, label, is_cont))
+ goto fail;
+ if (label != JS_ATOM_NULL) {
+ if (next_token(s))
+ goto fail;
+ }
+ if (js_parse_expect_semi(s))
+ goto fail;
+ }
+ break;
+ case TOK_SWITCH:
+ {
+ int label_case, label_break, label1;
+ int default_label_pos;
+ BlockEnv break_entry;
+
+ if (next_token(s))
+ goto fail;
+
+ set_eval_ret_undefined(s);
+ if (js_parse_expr_paren(s))
+ goto fail;
+
+ push_scope(s);
+ label_break = new_label(s);
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, -1, 1);
+
+ if (js_parse_expect(s, '{'))
+ goto fail;
+
+ default_label_pos = -1;
+ label_case = -1;
+ while (s->token.val != '}') {
+ if (s->token.val == TOK_CASE) {
+ label1 = -1;
+ if (label_case >= 0) {
+ /* skip the case if needed */
+ label1 = emit_goto(s, OP_goto, -1);
+ }
+ emit_label(s, label_case);
+ label_case = -1;
+ for (;;) {
+ /* parse a sequence of case clauses */
+ if (next_token(s))
+ goto fail;
+ emit_op(s, OP_dup);
+ if (js_parse_expr(s))
+ goto fail;
+ if (js_parse_expect(s, ':'))
+ goto fail;
+ emit_op(s, OP_strict_eq);
+ if (s->token.val == TOK_CASE) {
+ label1 = emit_goto(s, OP_if_true, label1);
+ } else {
+ label_case = emit_goto(s, OP_if_false, -1);
+ emit_label(s, label1);
+ break;
+ }
+ }
+ } else if (s->token.val == TOK_DEFAULT) {
+ if (next_token(s))
+ goto fail;
+ if (js_parse_expect(s, ':'))
+ goto fail;
+ if (default_label_pos >= 0) {
+ js_parse_error(s, "duplicate default");
+ goto fail;
+ }
+ if (label_case < 0) {
+ /* falling thru direct from switch expression */
+ label_case = emit_goto(s, OP_goto, -1);
+ }
+ /* Emit a dummy label opcode. Label will be patched after
+ the end of the switch body. Do not use emit_label(s, 0)
+ because it would clobber label 0 address, preventing
+ proper optimizer operation.
+ */
+ emit_op(s, OP_label);
+ emit_u32(s, 0);
+ default_label_pos = s->cur_func->byte_code.size - 4;
+ } else {
+ if (label_case < 0) {
+ /* falling thru direct from switch expression */
+ js_parse_error(s, "invalid switch statement");
+ goto fail;
+ }
+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
+ goto fail;
+ }
+ }
+ if (js_parse_expect(s, '}'))
+ goto fail;
+ if (default_label_pos >= 0) {
+ /* Ugly patch for the the `default` label, shameful and risky */
+ put_u32(s->cur_func->byte_code.buf + default_label_pos,
+ label_case);
+ s->cur_func->label_slots[label_case].pos = default_label_pos + 4;
+ } else {
+ emit_label(s, label_case);
+ }
+ emit_label(s, label_break);
+ emit_op(s, OP_drop); /* drop the switch expression */
+
+ pop_break_entry(s->cur_func);
+ pop_scope(s);
+ }
+ break;
+ case TOK_TRY:
+ {
+ int label_catch, label_catch2, label_finally, label_end;
+ JSAtom name;
+ BlockEnv block_env;
+
+ set_eval_ret_undefined(s);
+ if (next_token(s))
+ goto fail;
+ label_catch = new_label(s);
+ label_catch2 = new_label(s);
+ label_finally = new_label(s);
+ label_end = new_label(s);
+
+ emit_goto(s, OP_catch, label_catch);
+
+ push_break_entry(s->cur_func, &block_env,
+ JS_ATOM_NULL, -1, -1, 1);
+ block_env.label_finally = label_finally;
+
+ if (js_parse_block(s))
+ goto fail;
+
+ pop_break_entry(s->cur_func);
+
+ if (js_is_live_code(s)) {
+ /* drop the catch offset */
+ emit_op(s, OP_drop);
+ /* must push dummy value to keep same stack size */
+ emit_op(s, OP_undefined);
+ emit_goto(s, OP_gosub, label_finally);
+ emit_op(s, OP_drop);
+
+ emit_goto(s, OP_goto, label_end);
+ }
+
+ if (s->token.val == TOK_CATCH) {
+ if (next_token(s))
+ goto fail;
+
+ push_scope(s); /* catch variable */
+ emit_label(s, label_catch);
+
+ if (s->token.val == '{') {
+ /* support optional-catch-binding feature */
+ emit_op(s, OP_drop); /* pop the exception object */
+ } else {
+ if (js_parse_expect(s, '('))
+ goto fail;
+ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
+ if (s->token.val == '[' || s->token.val == '{') {
+ /* XXX: TOK_LET is not completely correct */
+ if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0)
+ goto fail;
+ } else {
+ js_parse_error(s, "identifier expected");
+ goto fail;
+ }
+ } else {
+ name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)
+ || js_define_var(s, name, TOK_CATCH) < 0) {
+ JS_FreeAtom(ctx, name);
+ goto fail;
+ }
+ /* store the exception value in the catch variable */
+ emit_op(s, OP_scope_put_var);
+ emit_u32(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+ if (js_parse_expect(s, ')'))
+ goto fail;
+ }
+ /* XXX: should keep the address to nop it out if there is no finally block */
+ emit_goto(s, OP_catch, label_catch2);
+
+ push_scope(s); /* catch block */
+ push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
+ -1, -1, 1);
+ block_env.label_finally = label_finally;
+
+ if (js_parse_block(s))
+ goto fail;
+
+ pop_break_entry(s->cur_func);
+ pop_scope(s); /* catch block */
+ pop_scope(s); /* catch variable */
+
+ if (js_is_live_code(s)) {
+ /* drop the catch2 offset */
+ emit_op(s, OP_drop);
+ /* XXX: should keep the address to nop it out if there is no finally block */
+ /* must push dummy value to keep same stack size */
+ emit_op(s, OP_undefined);
+ emit_goto(s, OP_gosub, label_finally);
+ emit_op(s, OP_drop);
+ emit_goto(s, OP_goto, label_end);
+ }
+ /* catch exceptions thrown in the catch block to execute the
+ * finally clause and rethrow the exception */
+ emit_label(s, label_catch2);
+ /* catch value is at TOS, no need to push undefined */
+ emit_goto(s, OP_gosub, label_finally);
+ emit_op(s, OP_throw);
+
+ } else if (s->token.val == TOK_FINALLY) {
+ /* finally without catch : execute the finally clause
+ * and rethrow the exception */
+ emit_label(s, label_catch);
+ /* catch value is at TOS, no need to push undefined */
+ emit_goto(s, OP_gosub, label_finally);
+ emit_op(s, OP_throw);
+ } else {
+ js_parse_error(s, "expecting catch or finally");
+ goto fail;
+ }
+ emit_label(s, label_finally);
+ if (s->token.val == TOK_FINALLY) {
+ int saved_eval_ret_idx = 0; /* avoid warning */
+
+ if (next_token(s))
+ goto fail;
+ /* on the stack: ret_value gosub_ret_value */
+ push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
+ -1, -1, 2);
+
+ if (s->cur_func->eval_ret_idx >= 0) {
+ /* 'finally' updates eval_ret only if not a normal
+ termination */
+ saved_eval_ret_idx =
+ add_var(s->ctx, s->cur_func, JS_ATOM__ret_);
+ if (saved_eval_ret_idx < 0)
+ goto fail;
+ emit_op(s, OP_get_loc);
+ emit_u16(s, s->cur_func->eval_ret_idx);
+ emit_op(s, OP_put_loc);
+ emit_u16(s, saved_eval_ret_idx);
+ set_eval_ret_undefined(s);
+ }
+
+ if (js_parse_block(s))
+ goto fail;
+
+ if (s->cur_func->eval_ret_idx >= 0) {
+ emit_op(s, OP_get_loc);
+ emit_u16(s, saved_eval_ret_idx);
+ emit_op(s, OP_put_loc);
+ emit_u16(s, s->cur_func->eval_ret_idx);
+ }
+ pop_break_entry(s->cur_func);
+ }
+ emit_op(s, OP_ret);
+ emit_label(s, label_end);
+ }
+ break;
+ case ';':
+ /* empty statement */
+ if (next_token(s))
+ goto fail;
+ break;
+ case TOK_WITH:
+ if (s->cur_func->js_mode & JS_MODE_STRICT) {
+ js_parse_error(s, "invalid keyword: with");
+ goto fail;
+ } else {
+ int with_idx;
+
+ if (next_token(s))
+ goto fail;
+
+ if (js_parse_expr_paren(s))
+ goto fail;
+
+ push_scope(s);
+ with_idx = define_var(s, s->cur_func, JS_ATOM__with_,
+ JS_VAR_DEF_WITH);
+ if (with_idx < 0)
+ goto fail;
+ emit_op(s, OP_to_object);
+ emit_op(s, OP_put_loc);
+ emit_u16(s, with_idx);
+
+ set_eval_ret_undefined(s);
+ if (js_parse_statement(s))
+ goto fail;
+
+ /* Popping scope drops lexical context for the with object variable */
+ pop_scope(s);
+ }
+ break;
+ case TOK_FUNCTION:
+ /* ES6 Annex B.3.2 and B.3.3 semantics */
+ if (!(decl_mask & DECL_MASK_FUNC))
+ goto func_decl_error;
+ if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')
+ goto func_decl_error;
+ goto parse_func_var;
+ case TOK_IDENT:
+ if (s->token.u.ident.is_reserved) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ /* Determine if `let` introduces a Declaration or an ExpressionStatement */
+ switch (is_let(s, decl_mask)) {
+ case TRUE:
+ tok = TOK_LET;
+ goto haslet;
+ case FALSE:
+ break;
+ default:
+ goto fail;
+ }
+ if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) == TOK_FUNCTION) {
+ if (!(decl_mask & DECL_MASK_OTHER)) {
+ func_decl_error:
+ js_parse_error(s, "function declarations can't appear in single-statement context");
+ goto fail;
+ }
+ parse_func_var:
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ goto fail;
+ break;
+ }
+ goto hasexpr;
+
+ case TOK_CLASS:
+ if (!(decl_mask & DECL_MASK_OTHER)) {
+ js_parse_error(s, "class declarations can't appear in single-statement context");
+ goto fail;
+ }
+ if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))
+ return -1;
+ break;
+
+ case TOK_DEBUGGER:
+ /* currently no debugger, so just skip the keyword */
+ if (next_token(s))
+ goto fail;
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+
+ case TOK_ENUM:
+ case TOK_EXPORT:
+ case TOK_EXTENDS:
+ js_unsupported_keyword(s, s->token.u.ident.atom);
+ goto fail;
+
+ default:
+ hasexpr:
+ if (js_parse_expr(s))
+ goto fail;
+ if (s->cur_func->eval_ret_idx >= 0) {
+ /* store the expression value so that it can be returned
+ by eval() */
+ emit_op(s, OP_put_loc);
+ emit_u16(s, s->cur_func->eval_ret_idx);
+ } else {
+ emit_op(s, OP_drop); /* drop the result */
+ }
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+ }
+done:
+ JS_FreeAtom(ctx, label_name);
+ return 0;
+fail:
+ JS_FreeAtom(ctx, label_name);
+ return -1;
+}
+
+/* 'name' is freed */
+static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
+{
+ JSModuleDef *m;
+ m = js_mallocz(ctx, sizeof(*m));
+ if (!m) {
+ JS_FreeAtom(ctx, name);
+ return NULL;
+ }
+ m->header.ref_count = 1;
+ m->module_name = name;
+ m->module_ns = JS_UNDEFINED;
+ m->func_obj = JS_UNDEFINED;
+ m->eval_exception = JS_UNDEFINED;
+ m->meta_obj = JS_UNDEFINED;
+ list_add_tail(&m->link, &ctx->loaded_modules);
+ return m;
+}
+
+static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
+ JS_MarkFunc *mark_func)
+{
+ int i;
+
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL &&
+ me->u.local.var_ref) {
+ mark_func(rt, &me->u.local.var_ref->header);
+ }
+ }
+
+ JS_MarkValue(rt, m->module_ns, mark_func);
+ JS_MarkValue(rt, m->func_obj, mark_func);
+ JS_MarkValue(rt, m->eval_exception, mark_func);
+ JS_MarkValue(rt, m->meta_obj, mark_func);
+}
+
+static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
+{
+ int i;
+
+ JS_FreeAtom(ctx, m->module_name);
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ JS_FreeAtom(ctx, rme->module_name);
+ }
+ js_free(ctx, m->req_module_entries);
+
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL)
+ free_var_ref(ctx->rt, me->u.local.var_ref);
+ JS_FreeAtom(ctx, me->export_name);
+ JS_FreeAtom(ctx, me->local_name);
+ }
+ js_free(ctx, m->export_entries);
+
+ js_free(ctx, m->star_export_entries);
+
+ for(i = 0; i < m->import_entries_count; i++) {
+ JSImportEntry *mi = &m->import_entries[i];
+ JS_FreeAtom(ctx, mi->import_name);
+ }
+ js_free(ctx, m->import_entries);
+
+ JS_FreeValue(ctx, m->module_ns);
+ JS_FreeValue(ctx, m->func_obj);
+ JS_FreeValue(ctx, m->eval_exception);
+ JS_FreeValue(ctx, m->meta_obj);
+ list_del(&m->link);
+ js_free(ctx, m);
+}
+
+static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
+ JSAtom module_name)
+{
+ JSReqModuleEntry *rme;
+ int i;
+
+ /* no need to add the module request if it is already present */
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ rme = &m->req_module_entries[i];
+ if (rme->module_name == module_name)
+ return i;
+ }
+
+ if (js_resize_array(ctx, (void **)&m->req_module_entries,
+ sizeof(JSReqModuleEntry),
+ &m->req_module_entries_size,
+ m->req_module_entries_count + 1))
+ return -1;
+ rme = &m->req_module_entries[m->req_module_entries_count++];
+ rme->module_name = JS_DupAtom(ctx, module_name);
+ rme->module = NULL;
+ return i;
+}
+
+static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,
+ JSAtom export_name)
+{
+ JSExportEntry *me;
+ int i;
+ for(i = 0; i < m->export_entries_count; i++) {
+ me = &m->export_entries[i];
+ if (me->export_name == export_name)
+ return me;
+ }
+ return NULL;
+}
+
+static JSExportEntry *add_export_entry2(JSContext *ctx,
+ JSParseState *s, JSModuleDef *m,
+ JSAtom local_name, JSAtom export_name,
+ JSExportTypeEnum export_type)
+{
+ JSExportEntry *me;
+
+ if (find_export_entry(ctx, m, export_name)) {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ if (s) {
+ js_parse_error(s, "duplicate exported name '%s'",
+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
+ } else {
+ JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);
+ }
+ return NULL;
+ }
+
+ if (js_resize_array(ctx, (void **)&m->export_entries,
+ sizeof(JSExportEntry),
+ &m->export_entries_size,
+ m->export_entries_count + 1))
+ return NULL;
+ me = &m->export_entries[m->export_entries_count++];
+ memset(me, 0, sizeof(*me));
+ me->local_name = JS_DupAtom(ctx, local_name);
+ me->export_name = JS_DupAtom(ctx, export_name);
+ me->export_type = export_type;
+ return me;
+}
+
+static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
+ JSAtom local_name, JSAtom export_name,
+ JSExportTypeEnum export_type)
+{
+ return add_export_entry2(s->ctx, s, m, local_name, export_name,
+ export_type);
+}
+
+static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
+ int req_module_idx)
+{
+ JSStarExportEntry *se;
+
+ if (js_resize_array(ctx, (void **)&m->star_export_entries,
+ sizeof(JSStarExportEntry),
+ &m->star_export_entries_size,
+ m->star_export_entries_count + 1))
+ return -1;
+ se = &m->star_export_entries[m->star_export_entries_count++];
+ se->req_module_idx = req_module_idx;
+ return 0;
+}
+
+/* create a C module */
+JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
+ JSModuleInitFunc *func)
+{
+ JSModuleDef *m;
+ JSAtom name;
+ name = JS_NewAtom(ctx, name_str);
+ if (name == JS_ATOM_NULL)
+ return NULL;
+ m = js_new_module_def(ctx, name);
+ m->init_func = func;
+ return m;
+}
+
+int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)
+{
+ JSExportEntry *me;
+ JSAtom name;
+ name = JS_NewAtom(ctx, export_name);
+ if (name == JS_ATOM_NULL)
+ return -1;
+ me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,
+ JS_EXPORT_TYPE_LOCAL);
+ JS_FreeAtom(ctx, name);
+ if (!me)
+ return -1;
+ else
+ return 0;
+}
+
+int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
+ JSValue val)
+{
+ JSExportEntry *me;
+ JSAtom name;
+ name = JS_NewAtom(ctx, export_name);
+ if (name == JS_ATOM_NULL)
+ goto fail;
+ me = find_export_entry(ctx, m, name);
+ JS_FreeAtom(ctx, name);
+ if (!me)
+ goto fail;
+ set_value(ctx, me->u.local.var_ref->pvalue, val);
+ return 0;
+ fail:
+ JS_FreeValue(ctx, val);
+ return -1;
+}
+
+void JS_SetModuleLoaderFunc(JSRuntime *rt,
+ JSModuleNormalizeFunc *module_normalize,
+ JSModuleLoaderFunc *module_loader, void *opaque)
+{
+ rt->module_normalize_func = module_normalize;
+ rt->module_loader_func = module_loader;
+ rt->module_loader_opaque = opaque;
+}
+
+/* default module filename normalizer */
+static char *js_default_module_normalize_name(JSContext *ctx,
+ const char *base_name,
+ const char *name)
+{
+ char *filename, *p;
+ const char *r;
+ int len;
+
+ if (name[0] != '.') {
+ /* if no initial dot, the module name is not modified */
+ return js_strdup(ctx, name);
+ }
+
+ p = strrchr(base_name, '/');
+ if (p)
+ len = p - base_name;
+ else
+ len = 0;
+
+ filename = js_malloc(ctx, len + strlen(name) + 1 + 1);
+ if (!filename)
+ return NULL;
+ memcpy(filename, base_name, len);
+ filename[len] = '\0';
+
+ /* we only normalize the leading '..' or '.' */
+ r = name;
+ for(;;) {
+ if (r[0] == '.' && r[1] == '/') {
+ r += 2;
+ } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {
+ /* remove the last path element of filename, except if "."
+ or ".." */
+ if (filename[0] == '\0')
+ break;
+ p = strrchr(filename, '/');
+ if (!p)
+ p = filename;
+ else
+ p++;
+ if (!strcmp(p, ".") || !strcmp(p, ".."))
+ break;
+ if (p > filename)
+ p--;
+ *p = '\0';
+ r += 3;
+ } else {
+ break;
+ }
+ }
+ if (filename[0] != '\0')
+ strcat(filename, "/");
+ strcat(filename, r);
+ // printf("normalize: %s %s -> %s\n", base_name, name, filename);
+ return filename;
+}
+
+static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)
+{
+ struct list_head *el;
+ JSModuleDef *m;
+
+ /* first look at the loaded modules */
+ list_for_each(el, &ctx->loaded_modules) {
+ m = list_entry(el, JSModuleDef, link);
+ if (m->module_name == name)
+ return m;
+ }
+ return NULL;
+}
+
+/* return NULL in case of exception (e.g. module could not be loaded) */
+static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
+ const char *base_cname,
+ const char *cname1)
+{
+ JSRuntime *rt = ctx->rt;
+ JSModuleDef *m;
+ char *cname;
+ JSAtom module_name;
+
+ if (!rt->module_normalize_func) {
+ cname = js_default_module_normalize_name(ctx, base_cname, cname1);
+ } else {
+ cname = rt->module_normalize_func(ctx, base_cname, cname1,
+ rt->module_loader_opaque);
+ }
+ if (!cname)
+ return NULL;
+
+ module_name = JS_NewAtom(ctx, cname);
+ if (module_name == JS_ATOM_NULL) {
+ js_free(ctx, cname);
+ return NULL;
+ }
+
+ /* first look at the loaded modules */
+ m = js_find_loaded_module(ctx, module_name);
+ if (m) {
+ js_free(ctx, cname);
+ JS_FreeAtom(ctx, module_name);
+ return m;
+ }
+
+ JS_FreeAtom(ctx, module_name);
+
+ /* load the module */
+ if (!rt->module_loader_func) {
+ /* XXX: use a syntax error ? */
+ JS_ThrowReferenceError(ctx, "could not load module '%s'",
+ cname);
+ js_free(ctx, cname);
+ return NULL;
+ }
+
+ m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);
+ js_free(ctx, cname);
+ return m;
+}
+
+static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,
+ JSAtom base_module_name,
+ JSAtom module_name1)
+{
+ const char *base_cname, *cname;
+ JSModuleDef *m;
+
+ base_cname = JS_AtomToCString(ctx, base_module_name);
+ if (!base_cname)
+ return NULL;
+ cname = JS_AtomToCString(ctx, module_name1);
+ if (!cname) {
+ JS_FreeCString(ctx, base_cname);
+ return NULL;
+ }
+ m = js_host_resolve_imported_module(ctx, base_cname, cname);
+ JS_FreeCString(ctx, base_cname);
+ JS_FreeCString(ctx, cname);
+ return m;
+}
+
+typedef struct JSResolveEntry {
+ JSModuleDef *module;
+ JSAtom name;
+} JSResolveEntry;
+
+typedef struct JSResolveState {
+ JSResolveEntry *array;
+ int size;
+ int count;
+} JSResolveState;
+
+static int find_resolve_entry(JSResolveState *s,
+ JSModuleDef *m, JSAtom name)
+{
+ int i;
+ for(i = 0; i < s->count; i++) {
+ JSResolveEntry *re = &s->array[i];
+ if (re->module == m && re->name == name)
+ return i;
+ }
+ return -1;
+}
+
+static int add_resolve_entry(JSContext *ctx, JSResolveState *s,
+ JSModuleDef *m, JSAtom name)
+{
+ JSResolveEntry *re;
+
+ if (js_resize_array(ctx, (void **)&s->array,
+ sizeof(JSResolveEntry),
+ &s->size, s->count + 1))
+ return -1;
+ re = &s->array[s->count++];
+ re->module = m;
+ re->name = JS_DupAtom(ctx, name);
+ return 0;
+}
+
+typedef enum JSResolveResultEnum {
+ JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */
+ JS_RESOLVE_RES_FOUND = 0,
+ JS_RESOLVE_RES_NOT_FOUND,
+ JS_RESOLVE_RES_CIRCULAR,
+ JS_RESOLVE_RES_AMBIGUOUS,
+} JSResolveResultEnum;
+
+static JSResolveResultEnum js_resolve_export1(JSContext *ctx,
+ JSModuleDef **pmodule,
+ JSExportEntry **pme,
+ JSModuleDef *m,
+ JSAtom export_name,
+ JSResolveState *s)
+{
+ JSExportEntry *me;
+
+ *pmodule = NULL;
+ *pme = NULL;
+ if (find_resolve_entry(s, m, export_name) >= 0)
+ return JS_RESOLVE_RES_CIRCULAR;
+ if (add_resolve_entry(ctx, s, m, export_name) < 0)
+ return JS_RESOLVE_RES_EXCEPTION;
+ me = find_export_entry(ctx, m, export_name);
+ if (me) {
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ /* local export */
+ *pmodule = m;
+ *pme = me;
+ return JS_RESOLVE_RES_FOUND;
+ } else {
+ /* indirect export */
+ JSModuleDef *m1;
+ m1 = m->req_module_entries[me->u.req_module_idx].module;
+ if (me->local_name == JS_ATOM__star_) {
+ /* export ns from */
+ *pmodule = m;
+ *pme = me;
+ return JS_RESOLVE_RES_FOUND;
+ } else {
+ return js_resolve_export1(ctx, pmodule, pme, m1,
+ me->local_name, s);
+ }
+ }
+ } else {
+ if (export_name != JS_ATOM_default) {
+ /* not found in direct or indirect exports: try star exports */
+ int i;
+
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ JSModuleDef *m1, *res_m;
+ JSExportEntry *res_me;
+ JSResolveResultEnum ret;
+
+ m1 = m->req_module_entries[se->req_module_idx].module;
+ ret = js_resolve_export1(ctx, &res_m, &res_me, m1,
+ export_name, s);
+ if (ret == JS_RESOLVE_RES_AMBIGUOUS ||
+ ret == JS_RESOLVE_RES_EXCEPTION) {
+ return ret;
+ } else if (ret == JS_RESOLVE_RES_FOUND) {
+ if (*pme != NULL) {
+ if (*pmodule != res_m ||
+ res_me->local_name != (*pme)->local_name) {
+ *pmodule = NULL;
+ *pme = NULL;
+ return JS_RESOLVE_RES_AMBIGUOUS;
+ }
+ } else {
+ *pmodule = res_m;
+ *pme = res_me;
+ }
+ }
+ }
+ if (*pme != NULL)
+ return JS_RESOLVE_RES_FOUND;
+ }
+ return JS_RESOLVE_RES_NOT_FOUND;
+ }
+}
+
+/* If the return value is JS_RESOLVE_RES_FOUND, return the module
+ (*pmodule) and the corresponding local export entry
+ (*pme). Otherwise return (NULL, NULL) */
+static JSResolveResultEnum js_resolve_export(JSContext *ctx,
+ JSModuleDef **pmodule,
+ JSExportEntry **pme,
+ JSModuleDef *m,
+ JSAtom export_name)
+{
+ JSResolveState ss, *s = &ss;
+ int i;
+ JSResolveResultEnum ret;
+
+ s->array = NULL;
+ s->size = 0;
+ s->count = 0;
+
+ ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);
+
+ for(i = 0; i < s->count; i++)
+ JS_FreeAtom(ctx, s->array[i].name);
+ js_free(ctx, s->array);
+
+ return ret;
+}
+
+static void js_resolve_export_throw_error(JSContext *ctx,
+ JSResolveResultEnum res,
+ JSModuleDef *m, JSAtom export_name)
+{
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ char buf2[ATOM_GET_STR_BUF_SIZE];
+ switch(res) {
+ case JS_RESOLVE_RES_EXCEPTION:
+ break;
+ default:
+ case JS_RESOLVE_RES_NOT_FOUND:
+ JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
+ break;
+ case JS_RESOLVE_RES_CIRCULAR:
+ JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
+ break;
+ case JS_RESOLVE_RES_AMBIGUOUS:
+ JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
+ break;
+ }
+}
+
+
+typedef enum {
+ EXPORTED_NAME_AMBIGUOUS,
+ EXPORTED_NAME_NORMAL,
+ EXPORTED_NAME_NS,
+} ExportedNameEntryEnum;
+
+typedef struct ExportedNameEntry {
+ JSAtom export_name;
+ ExportedNameEntryEnum export_type;
+ union {
+ JSExportEntry *me; /* using when the list is built */
+ JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
+ JSModuleDef *module; /* for EXPORTED_NAME_NS */
+ } u;
+} ExportedNameEntry;
+
+typedef struct GetExportNamesState {
+ JSModuleDef **modules;
+ int modules_size;
+ int modules_count;
+
+ ExportedNameEntry *exported_names;
+ int exported_names_size;
+ int exported_names_count;
+} GetExportNamesState;
+
+static int find_exported_name(GetExportNamesState *s, JSAtom name)
+{
+ int i;
+ for(i = 0; i < s->exported_names_count; i++) {
+ if (s->exported_names[i].export_name == name)
+ return i;
+ }
+ return -1;
+}
+
+static warn_unused int get_exported_names(JSContext *ctx,
+ GetExportNamesState *s,
+ JSModuleDef *m, BOOL from_star)
+{
+ ExportedNameEntry *en;
+ int i, j;
+
+ /* check circular reference */
+ for(i = 0; i < s->modules_count; i++) {
+ if (s->modules[i] == m)
+ return 0;
+ }
+ if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),
+ &s->modules_size, s->modules_count + 1))
+ return -1;
+ s->modules[s->modules_count++] = m;
+
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (from_star && me->export_name == JS_ATOM_default)
+ continue;
+ j = find_exported_name(s, me->export_name);
+ if (j < 0) {
+ if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),
+ &s->exported_names_size,
+ s->exported_names_count + 1))
+ return -1;
+ en = &s->exported_names[s->exported_names_count++];
+ en->export_name = me->export_name;
+ /* avoid a second lookup for simple module exports */
+ if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)
+ en->u.me = NULL;
+ else
+ en->u.me = me;
+ } else {
+ en = &s->exported_names[j];
+ en->u.me = NULL;
+ }
+ }
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ JSModuleDef *m1;
+ m1 = m->req_module_entries[se->req_module_idx].module;
+ if (get_exported_names(ctx, s, m1, TRUE))
+ return -1;
+ }
+ return 0;
+}
+
+/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */
+static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
+{
+ return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);
+}
+
+static const JSClassExoticMethods js_module_ns_exotic_methods = {
+ .has_property = js_module_ns_has,
+};
+
+static int exported_names_cmp(const void *p1, const void *p2, void *opaque)
+{
+ JSContext *ctx = opaque;
+ const ExportedNameEntry *me1 = p1;
+ const ExportedNameEntry *me2 = p2;
+ JSValue str1, str2;
+ int ret;
+
+ /* XXX: should avoid allocation memory in atom comparison */
+ str1 = JS_AtomToString(ctx, me1->export_name);
+ str2 = JS_AtomToString(ctx, me2->export_name);
+ if (JS_IsException(str1) || JS_IsException(str2)) {
+ /* XXX: raise an error ? */
+ ret = 0;
+ } else {
+ ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),
+ JS_VALUE_GET_STRING(str2));
+ }
+ JS_FreeValue(ctx, str1);
+ JS_FreeValue(ctx, str2);
+ return ret;
+}
+
+static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m);
+
+static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
+ void *opaque)
+{
+ JSModuleDef *m = opaque;
+ return js_get_module_ns(ctx, m);
+}
+
+static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
+{
+ JSValue obj;
+ JSObject *p;
+ GetExportNamesState s_s, *s = &s_s;
+ int i, ret;
+ JSProperty *pr;
+
+ obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);
+ if (JS_IsException(obj))
+ return obj;
+ p = JS_VALUE_GET_OBJ(obj);
+
+ memset(s, 0, sizeof(*s));
+ ret = get_exported_names(ctx, s, m, FALSE);
+ js_free(ctx, s->modules);
+ if (ret)
+ goto fail;
+
+ /* Resolve the exported names. The ambiguous exports are removed */
+ for(i = 0; i < s->exported_names_count; i++) {
+ ExportedNameEntry *en = &s->exported_names[i];
+ JSResolveResultEnum res;
+ JSExportEntry *res_me;
+ JSModuleDef *res_m;
+
+ if (en->u.me) {
+ res_me = en->u.me; /* fast case: no resolution needed */
+ res_m = m;
+ res = JS_RESOLVE_RES_FOUND;
+ } else {
+ res = js_resolve_export(ctx, &res_m, &res_me, m,
+ en->export_name);
+ }
+ if (res != JS_RESOLVE_RES_FOUND) {
+ if (res != JS_RESOLVE_RES_AMBIGUOUS) {
+ js_resolve_export_throw_error(ctx, res, m, en->export_name);
+ goto fail;
+ }
+ en->export_type = EXPORTED_NAME_AMBIGUOUS;
+ } else {
+ if (res_me->local_name == JS_ATOM__star_) {
+ en->export_type = EXPORTED_NAME_NS;
+ en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;
+ } else {
+ en->export_type = EXPORTED_NAME_NORMAL;
+ if (res_me->u.local.var_ref) {
+ en->u.var_ref = res_me->u.local.var_ref;
+ } else {
+ JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
+ en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
+ }
+ }
+ }
+ }
+
+ /* sort the exported names */
+ rqsort(s->exported_names, s->exported_names_count,
+ sizeof(s->exported_names[0]), exported_names_cmp, ctx);
+
+ for(i = 0; i < s->exported_names_count; i++) {
+ ExportedNameEntry *en = &s->exported_names[i];
+ switch(en->export_type) {
+ case EXPORTED_NAME_NORMAL:
+ {
+ JSVarRef *var_ref = en->u.var_ref;
+ pr = add_property(ctx, p, en->export_name,
+ JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
+ JS_PROP_VARREF);
+ if (!pr)
+ goto fail;
+ var_ref->header.ref_count++;
+ pr->u.var_ref = var_ref;
+ }
+ break;
+ case EXPORTED_NAME_NS:
+ /* the exported namespace must be created on demand */
+ if (JS_DefineAutoInitProperty(ctx, obj,
+ en->export_name,
+ JS_AUTOINIT_ID_MODULE_NS,
+ en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
+ goto fail;
+ break;
+ default:
+ break;
+ }
+ }
+
+ js_free(ctx, s->exported_names);
+
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,
+ JS_AtomToString(ctx, JS_ATOM_Module),
+ 0);
+
+ p->extensible = FALSE;
+ return obj;
+ fail:
+ js_free(ctx, s->exported_names);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m)
+{
+ if (JS_IsUndefined(m->module_ns)) {
+ JSValue val;
+ val = js_build_module_ns(ctx, m);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ m->module_ns = val;
+ }
+ return JS_DupValue(ctx, m->module_ns);
+}
+
+/* Load all the required modules for module 'm' */
+static int js_resolve_module(JSContext *ctx, JSModuleDef *m)
+{
+ int i;
+ JSModuleDef *m1;
+
+ if (m->resolved)
+ return 0;
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ }
+#endif
+ m->resolved = TRUE;
+ /* resolve each requested module */
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,
+ rme->module_name);
+ if (!m1)
+ return -1;
+ rme->module = m1;
+ /* already done in js_host_resolve_imported_module() except if
+ the module was loaded with JS_EvalBinary() */
+ if (js_resolve_module(ctx, m1) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)
+{
+ JSVarRef *var_ref;
+ var_ref = js_malloc(ctx, sizeof(JSVarRef));
+ if (!var_ref)
+ return NULL;
+ var_ref->header.ref_count = 1;
+ if (is_lexical)
+ var_ref->value = JS_UNINITIALIZED;
+ else
+ var_ref->value = JS_UNDEFINED;
+ var_ref->pvalue = &var_ref->value;
+ var_ref->is_detached = TRUE;
+ add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
+ return var_ref;
+}
+
+/* Create the <eval> function associated with the module */
+static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
+{
+ JSFunctionBytecode *b;
+ int i;
+ JSVarRef **var_refs;
+ JSValue func_obj, bfunc;
+ JSObject *p;
+
+ bfunc = m->func_obj;
+ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_BYTECODE_FUNCTION);
+
+ if (JS_IsException(func_obj))
+ return -1;
+ b = JS_VALUE_GET_PTR(bfunc);
+
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->u.func.function_bytecode = b;
+ b->header.ref_count++;
+ p->u.func.home_object = NULL;
+ p->u.func.var_refs = NULL;
+ if (b->closure_var_count) {
+ var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
+ if (!var_refs)
+ goto fail;
+ p->u.func.var_refs = var_refs;
+
+ /* create the global variables. The other variables are
+ imported from other modules */
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ JSVarRef *var_ref;
+ if (cv->is_local) {
+ var_ref = js_create_module_var(ctx, cv->is_lexical);
+ if (!var_ref)
+ goto fail;
+#ifdef DUMP_MODULE_RESOLVE
+ printf("local %d: %p\n", i, var_ref);
+#endif
+ var_refs[i] = var_ref;
+ }
+ }
+ }
+ m->func_obj = func_obj;
+ JS_FreeValue(ctx, bfunc);
+ return 0;
+ fail:
+ JS_FreeValue(ctx, func_obj);
+ return -1;
+}
+
+/* must be done before js_link_module() because of cyclic references */
+static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
+{
+ BOOL is_c_module;
+ int i;
+ JSVarRef *var_ref;
+
+ if (m->func_created)
+ return 0;
+
+ is_c_module = (m->init_func != NULL);
+
+ if (is_c_module) {
+ /* initialize the exported variables */
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ var_ref = js_create_module_var(ctx, FALSE);
+ if (!var_ref)
+ return -1;
+ me->u.local.var_ref = var_ref;
+ }
+ }
+ } else {
+ if (js_create_module_bytecode_function(ctx, m))
+ return -1;
+ }
+ m->func_created = TRUE;
+
+ /* do it on the dependencies */
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ if (js_create_module_function(ctx, rme->module) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Prepare a module to be executed by resolving all the imported
+ variables. */
+static int js_link_module(JSContext *ctx, JSModuleDef *m)
+{
+ int i;
+ JSImportEntry *mi;
+ JSModuleDef *m1;
+ JSVarRef **var_refs, *var_ref;
+ JSObject *p;
+ BOOL is_c_module;
+ JSValue ret_val;
+
+ if (m->instantiated)
+ return 0;
+ m->instantiated = TRUE;
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ }
+#endif
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ if (js_link_module(ctx, rme->module) < 0)
+ goto fail;
+ }
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ }
+#endif
+ /* check the indirect exports */
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&
+ me->local_name != JS_ATOM__star_) {
+ JSResolveResultEnum ret;
+ JSExportEntry *res_me;
+ JSModuleDef *res_m, *m1;
+ m1 = m->req_module_entries[me->u.req_module_idx].module;
+ ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);
+ if (ret != JS_RESOLVE_RES_FOUND) {
+ js_resolve_export_throw_error(ctx, ret, m, me->export_name);
+ goto fail;
+ }
+ }
+ }
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ printf("exported bindings:\n");
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ printf(" name="); print_atom(ctx, me->export_name);
+ printf(" local="); print_atom(ctx, me->local_name);
+ printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);
+ }
+ }
+#endif
+
+ is_c_module = (m->init_func != NULL);
+
+ if (!is_c_module) {
+ p = JS_VALUE_GET_OBJ(m->func_obj);
+ var_refs = p->u.func.var_refs;
+
+ for(i = 0; i < m->import_entries_count; i++) {
+ mi = &m->import_entries[i];
+#ifdef DUMP_MODULE_RESOLVE
+ printf("import var_idx=%d name=", mi->var_idx);
+ print_atom(ctx, mi->import_name);
+ printf(": ");
+#endif
+ m1 = m->req_module_entries[mi->req_module_idx].module;
+ if (mi->import_name == JS_ATOM__star_) {
+ JSValue val;
+ /* name space import */
+ val = js_get_module_ns(ctx, m1);
+ if (JS_IsException(val))
+ goto fail;
+ set_value(ctx, &var_refs[mi->var_idx]->value, val);
+#ifdef DUMP_MODULE_RESOLVE
+ printf("namespace\n");
+#endif
+ } else {
+ JSResolveResultEnum ret;
+ JSExportEntry *res_me;
+ JSModuleDef *res_m;
+ JSObject *p1;
+
+ ret = js_resolve_export(ctx, &res_m,
+ &res_me, m1, mi->import_name);
+ if (ret != JS_RESOLVE_RES_FOUND) {
+ js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);
+ goto fail;
+ }
+ if (res_me->local_name == JS_ATOM__star_) {
+ JSValue val;
+ JSModuleDef *m2;
+ /* name space import from */
+ m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;
+ val = js_get_module_ns(ctx, m2);
+ if (JS_IsException(val))
+ goto fail;
+ var_ref = js_create_module_var(ctx, TRUE);
+ if (!var_ref) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ set_value(ctx, &var_ref->value, val);
+ var_refs[mi->var_idx] = var_ref;
+#ifdef DUMP_MODULE_RESOLVE
+ printf("namespace from\n");
+#endif
+ } else {
+ var_ref = res_me->u.local.var_ref;
+ if (!var_ref) {
+ p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
+ var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
+ }
+ var_ref->header.ref_count++;
+ var_refs[mi->var_idx] = var_ref;
+#ifdef DUMP_MODULE_RESOLVE
+ printf("local export (var_ref=%p)\n", var_ref);
+#endif
+ }
+ }
+ }
+
+ /* keep the exported variables in the module export entries (they
+ are used when the eval function is deleted and cannot be
+ initialized before in case imports are exported) */
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ var_ref = var_refs[me->u.local.var_idx];
+ var_ref->header.ref_count++;
+ me->u.local.var_ref = var_ref;
+ }
+ }
+
+ /* initialize the global variables */
+ ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL);
+ if (JS_IsException(ret_val))
+ goto fail;
+ JS_FreeValue(ctx, ret_val);
+ }
+
+#ifdef DUMP_MODULE_RESOLVE
+ printf("done instantiate\n");
+#endif
+ return 0;
+ fail:
+ return -1;
+}
+
+/* return JS_ATOM_NULL if the name cannot be found. Only works with
+ not striped bytecode functions. */
+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
+{
+ JSStackFrame *sf;
+ JSFunctionBytecode *b;
+ JSObject *p;
+ /* XXX: currently we just use the filename of the englobing
+ function. It does not work for eval(). Need to add a
+ ScriptOrModule info in JSFunctionBytecode */
+ sf = ctx->rt->current_stack_frame;
+ if (!sf)
+ return JS_ATOM_NULL;
+ while (n_stack_levels-- > 0) {
+ sf = sf->prev_frame;
+ if (!sf)
+ return JS_ATOM_NULL;
+ }
+ if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
+ return JS_ATOM_NULL;
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ if (!js_class_has_bytecode(p->class_id))
+ return JS_ATOM_NULL;
+ b = p->u.func.function_bytecode;
+ if (!b->has_debug)
+ return JS_ATOM_NULL;
+ return JS_DupAtom(ctx, b->debug.filename);
+}
+
+JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
+{
+ return JS_DupAtom(ctx, m->module_name);
+}
+
+JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)
+{
+ JSValue obj;
+ /* allocate meta_obj only if requested to save memory */
+ obj = m->meta_obj;
+ if (JS_IsUndefined(obj)) {
+ obj = JS_NewObjectProto(ctx, JS_NULL);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ m->meta_obj = obj;
+ }
+ return JS_DupValue(ctx, obj);
+}
+
+static JSValue js_import_meta(JSContext *ctx)
+{
+ JSAtom filename;
+ JSModuleDef *m;
+
+ filename = JS_GetScriptOrModuleName(ctx, 0);
+ if (filename == JS_ATOM_NULL)
+ goto fail;
+
+ /* XXX: inefficient, need to add a module or script pointer in
+ JSFunctionBytecode */
+ m = js_find_loaded_module(ctx, filename);
+ JS_FreeAtom(ctx, filename);
+ if (!m) {
+ fail:
+ JS_ThrowTypeError(ctx, "import.meta not supported in this context");
+ return JS_EXCEPTION;
+ }
+ return JS_GetImportMeta(ctx, m);
+}
+
+/* used by os.Worker() and import() */
+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
+ const char *filename)
+{
+ JSModuleDef *m;
+ JSValue ret, func_obj;
+
+ m = js_host_resolve_imported_module(ctx, basename, filename);
+ if (!m)
+ return NULL;
+
+ if (js_resolve_module(ctx, m) < 0) {
+ js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
+ return NULL;
+ }
+
+ /* Evaluate the module code */
+ func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ ret = JS_EvalFunction(ctx, func_obj);
+ if (JS_IsException(ret))
+ return NULL;
+ JS_FreeValue(ctx, ret);
+ return m;
+}
+
+static JSValue js_dynamic_import_job(JSContext *ctx,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst *resolving_funcs = argv;
+ JSValueConst basename_val = argv[2];
+ JSValueConst specifier = argv[3];
+ JSModuleDef *m;
+ const char *basename = NULL, *filename;
+ JSValue ret, err, ns;
+
+ if (!JS_IsString(basename_val)) {
+ JS_ThrowTypeError(ctx, "no function filename for import()");
+ goto exception;
+ }
+ basename = JS_ToCString(ctx, basename_val);
+ if (!basename)
+ goto exception;
+
+ filename = JS_ToCString(ctx, specifier);
+ if (!filename)
+ goto exception;
+
+ m = JS_RunModule(ctx, basename, filename);
+ JS_FreeCString(ctx, filename);
+ if (!m)
+ goto exception;
+
+ /* return the module namespace */
+ ns = js_get_module_ns(ctx, m);
+ if (JS_IsException(ns))
+ goto exception;
+
+ ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
+ 1, &ns);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, ns);
+ JS_FreeCString(ctx, basename);
+ return JS_UNDEFINED;
+ exception:
+
+ err = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+ 1, &err);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, err);
+ JS_FreeCString(ctx, basename);
+ return JS_UNDEFINED;
+}
+
+static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
+{
+ JSAtom basename;
+ JSValue promise, resolving_funcs[2], basename_val;
+ JSValueConst args[4];
+
+ basename = JS_GetScriptOrModuleName(ctx, 0);
+ if (basename == JS_ATOM_NULL)
+ basename_val = JS_NULL;
+ else
+ basename_val = JS_AtomToValue(ctx, basename);
+ JS_FreeAtom(ctx, basename);
+ if (JS_IsException(basename_val))
+ return basename_val;
+
+ promise = JS_NewPromiseCapability(ctx, resolving_funcs);
+ if (JS_IsException(promise)) {
+ JS_FreeValue(ctx, basename_val);
+ return promise;
+ }
+
+ args[0] = resolving_funcs[0];
+ args[1] = resolving_funcs[1];
+ args[2] = basename_val;
+ args[3] = specifier;
+
+ JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);
+
+ JS_FreeValue(ctx, basename_val);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return promise;
+}
+
+/* Run the <eval> function of the module and of all its requested
+ modules. */
+static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
+{
+ JSModuleDef *m1;
+ int i;
+ JSValue ret_val;
+
+ if (m->eval_mark)
+ return JS_UNDEFINED; /* avoid cycles */
+
+ if (m->evaluated) {
+ /* if the module was already evaluated, rethrow the exception
+ it raised */
+ if (m->eval_has_exception) {
+ return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));
+ } else {
+ return JS_UNDEFINED;
+ }
+ }
+
+ m->eval_mark = TRUE;
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ m1 = rme->module;
+ if (!m1->eval_mark) {
+ ret_val = js_evaluate_module(ctx, m1);
+ if (JS_IsException(ret_val)) {
+ m->eval_mark = FALSE;
+ return ret_val;
+ }
+ JS_FreeValue(ctx, ret_val);
+ }
+ }
+
+ if (m->init_func) {
+ /* C module init */
+ if (m->init_func(ctx, m) < 0)
+ ret_val = JS_EXCEPTION;
+ else
+ ret_val = JS_UNDEFINED;
+ } else {
+ ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);
+ m->func_obj = JS_UNDEFINED;
+ }
+ if (JS_IsException(ret_val)) {
+ /* save the thrown exception value */
+ m->eval_has_exception = TRUE;
+ m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);
+ }
+ m->eval_mark = FALSE;
+ m->evaluated = TRUE;
+ return ret_val;
+}
+
+static warn_unused JSAtom js_parse_from_clause(JSParseState *s)
+{
+ JSAtom module_name;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
+ js_parse_error(s, "from clause expected");
+ return JS_ATOM_NULL;
+ }
+ if (next_token(s))
+ return JS_ATOM_NULL;
+ if (s->token.val != TOK_STRING) {
+ js_parse_error(s, "string expected");
+ return JS_ATOM_NULL;
+ }
+ module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
+ if (module_name == JS_ATOM_NULL)
+ return JS_ATOM_NULL;
+ if (next_token(s)) {
+ JS_FreeAtom(s->ctx, module_name);
+ return JS_ATOM_NULL;
+ }
+ return module_name;
+}
+
+static warn_unused int js_parse_export(JSParseState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSModuleDef *m = s->cur_func->module;
+ JSAtom local_name, export_name;
+ int first_export, idx, i, tok;
+ JSAtom module_name;
+ JSExportEntry *me;
+
+ if (next_token(s))
+ return -1;
+
+ tok = s->token.val;
+ if (tok == TOK_CLASS) {
+ return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);
+ } else if (tok == TOK_FUNCTION ||
+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) == TOK_FUNCTION)) {
+ return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num,
+ JS_PARSE_EXPORT_NAMED, NULL);
+ }
+
+ if (next_token(s))
+ return -1;
+
+ switch(tok) {
+ case '{':
+ first_export = m->export_entries_count;
+ while (s->token.val != '}') {
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ return -1;
+ }
+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ export_name = JS_ATOM_NULL;
+ if (next_token(s))
+ goto fail;
+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
+ if (next_token(s))
+ goto fail;
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ goto fail;
+ }
+ export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)) {
+ fail:
+ JS_FreeAtom(ctx, local_name);
+ fail1:
+ JS_FreeAtom(ctx, export_name);
+ return -1;
+ }
+ } else {
+ export_name = JS_DupAtom(ctx, local_name);
+ }
+ me = add_export_entry(s, m, local_name, export_name,
+ JS_EXPORT_TYPE_LOCAL);
+ JS_FreeAtom(ctx, local_name);
+ JS_FreeAtom(ctx, export_name);
+ if (!me)
+ return -1;
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ return -1;
+ }
+ if (js_parse_expect(s, '}'))
+ return -1;
+ if (token_is_pseudo_keyword(s, JS_ATOM_from)) {
+ module_name = js_parse_from_clause(s);
+ if (module_name == JS_ATOM_NULL)
+ return -1;
+ idx = add_req_module_entry(ctx, m, module_name);
+ JS_FreeAtom(ctx, module_name);
+ if (idx < 0)
+ return -1;
+ for(i = first_export; i < m->export_entries_count; i++) {
+ me = &m->export_entries[i];
+ me->export_type = JS_EXPORT_TYPE_INDIRECT;
+ me->u.req_module_idx = idx;
+ }
+ }
+ break;
+ case '*':
+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
+ /* export ns from */
+ if (next_token(s))
+ return -1;
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ return -1;
+ }
+ export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ module_name = js_parse_from_clause(s);
+ if (module_name == JS_ATOM_NULL)
+ goto fail1;
+ idx = add_req_module_entry(ctx, m, module_name);
+ JS_FreeAtom(ctx, module_name);
+ if (idx < 0)
+ goto fail1;
+ me = add_export_entry(s, m, JS_ATOM__star_, export_name,
+ JS_EXPORT_TYPE_INDIRECT);
+ JS_FreeAtom(ctx, export_name);
+ if (!me)
+ return -1;
+ me->u.req_module_idx = idx;
+ } else {
+ module_name = js_parse_from_clause(s);
+ if (module_name == JS_ATOM_NULL)
+ return -1;
+ idx = add_req_module_entry(ctx, m, module_name);
+ JS_FreeAtom(ctx, module_name);
+ if (idx < 0)
+ return -1;
+ if (add_star_export_entry(ctx, m, idx) < 0)
+ return -1;
+ }
+ break;
+ case TOK_DEFAULT:
+ if (s->token.val == TOK_CLASS) {
+ return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);
+ } else if (s->token.val == TOK_FUNCTION ||
+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) == TOK_FUNCTION)) {
+ return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num,
+ JS_PARSE_EXPORT_DEFAULT, NULL);
+ } else {
+ if (js_parse_assign_expr(s))
+ return -1;
+ }
+ /* set the name of anonymous functions */
+ set_object_name(s, JS_ATOM_default);
+
+ /* store the value in the _default_ global variable and export
+ it */
+ local_name = JS_ATOM__default_;
+ if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)
+ return -1;
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, local_name);
+ emit_u16(s, 0);
+
+ if (!add_export_entry(s, m, local_name, JS_ATOM_default,
+ JS_EXPORT_TYPE_LOCAL))
+ return -1;
+ break;
+ case TOK_VAR:
+ case TOK_LET:
+ case TOK_CONST:
+ return js_parse_var(s, TRUE, tok, TRUE);
+ default:
+ return js_parse_error(s, "invalid export syntax");
+ }
+ return js_parse_expect_semi(s);
+}
+
+static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
+ BOOL is_local, BOOL is_arg,
+ int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind);
+
+static int add_import(JSParseState *s, JSModuleDef *m,
+ JSAtom local_name, JSAtom import_name)
+{
+ JSContext *ctx = s->ctx;
+ int i, var_idx;
+ JSImportEntry *mi;
+ BOOL is_local;
+
+ if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)
+ return js_parse_error(s, "invalid import binding");
+
+ if (local_name != JS_ATOM_default) {
+ for (i = 0; i < s->cur_func->closure_var_count; i++) {
+ if (s->cur_func->closure_var[i].var_name == local_name)
+ return js_parse_error(s, "duplicate import binding");
+ }
+ }
+
+ is_local = (import_name == JS_ATOM__star_);
+ var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,
+ m->import_entries_count,
+ local_name, TRUE, TRUE, FALSE);
+ if (var_idx < 0)
+ return -1;
+ if (js_resize_array(ctx, (void **)&m->import_entries,
+ sizeof(JSImportEntry),
+ &m->import_entries_size,
+ m->import_entries_count + 1))
+ return -1;
+ mi = &m->import_entries[m->import_entries_count++];
+ mi->import_name = JS_DupAtom(ctx, import_name);
+ mi->var_idx = var_idx;
+ return 0;
+}
+
+static warn_unused int js_parse_import(JSParseState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSModuleDef *m = s->cur_func->module;
+ JSAtom local_name, import_name, module_name;
+ int first_import, i, idx;
+
+ if (next_token(s))
+ return -1;
+
+ first_import = m->import_entries_count;
+ if (s->token.val == TOK_STRING) {
+ module_name = JS_ValueToAtom(ctx, s->token.u.str.str);
+ if (module_name == JS_ATOM_NULL)
+ return -1;
+ if (next_token(s)) {
+ JS_FreeAtom(ctx, module_name);
+ return -1;
+ }
+ } else {
+ if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved) {
+ return js_parse_error_reserved_identifier(s);
+ }
+ /* "default" import */
+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ import_name = JS_ATOM_default;
+ if (next_token(s))
+ goto fail;
+ if (add_import(s, m, local_name, import_name))
+ goto fail;
+ JS_FreeAtom(ctx, local_name);
+
+ if (s->token.val != ',')
+ goto end_import_clause;
+ if (next_token(s))
+ return -1;
+ }
+
+ if (s->token.val == '*') {
+ /* name space import */
+ if (next_token(s))
+ return -1;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_as))
+ return js_parse_error(s, "expecting 'as'");
+ if (next_token(s))
+ return -1;
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ return -1;
+ }
+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ import_name = JS_ATOM__star_;
+ if (next_token(s))
+ goto fail;
+ if (add_import(s, m, local_name, import_name))
+ goto fail;
+ JS_FreeAtom(ctx, local_name);
+ } else if (s->token.val == '{') {
+ if (next_token(s))
+ return -1;
+
+ while (s->token.val != '}') {
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ return -1;
+ }
+ import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ local_name = JS_ATOM_NULL;
+ if (next_token(s))
+ goto fail;
+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
+ if (next_token(s))
+ goto fail;
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ goto fail;
+ }
+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)) {
+ fail:
+ JS_FreeAtom(ctx, local_name);
+ JS_FreeAtom(ctx, import_name);
+ return -1;
+ }
+ } else {
+ local_name = JS_DupAtom(ctx, import_name);
+ }
+ if (add_import(s, m, local_name, import_name))
+ goto fail;
+ JS_FreeAtom(ctx, local_name);
+ JS_FreeAtom(ctx, import_name);
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ return -1;
+ }
+ if (js_parse_expect(s, '}'))
+ return -1;
+ }
+ end_import_clause:
+ module_name = js_parse_from_clause(s);
+ if (module_name == JS_ATOM_NULL)
+ return -1;
+ }
+ idx = add_req_module_entry(ctx, m, module_name);
+ JS_FreeAtom(ctx, module_name);
+ if (idx < 0)
+ return -1;
+ for(i = first_import; i < m->import_entries_count; i++)
+ m->import_entries[i].req_module_idx = idx;
+
+ return js_parse_expect_semi(s);
+}
+
+static warn_unused int js_parse_source_element(JSParseState *s)
+{
+ JSFunctionDef *fd = s->cur_func;
+ int tok;
+
+ if (s->token.val == TOK_FUNCTION ||
+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) == TOK_FUNCTION)) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ return -1;
+ } else if (s->token.val == TOK_EXPORT && fd->module) {
+ if (js_parse_export(s))
+ return -1;
+ } else if (s->token.val == TOK_IMPORT && fd->module &&
+ ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) {
+ /* the peek_token is needed to avoid confusion with ImportCall
+ (dynamic import) or import.meta */
+ if (js_parse_import(s))
+ return -1;
+ } else {
+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
+ return -1;
+ }
+ return 0;
+}
+
+static JSFunctionDef *js_new_function_def(JSContext *ctx,
+ JSFunctionDef *parent,
+ BOOL is_eval,
+ BOOL is_func_expr,
+ const char *filename, int line_num)
+{
+ JSFunctionDef *fd;
+
+ fd = js_mallocz(ctx, sizeof(*fd));
+ if (!fd)
+ return NULL;
+
+ fd->ctx = ctx;
+ init_list_head(&fd->child_list);
+
+ /* insert in parent list */
+ fd->parent = parent;
+ fd->parent_cpool_idx = -1;
+ if (parent) {
+ list_add_tail(&fd->link, &parent->child_list);
+ fd->js_mode = parent->js_mode;
+ fd->parent_scope_level = parent->scope_level;
+ }
+
+ fd->is_eval = is_eval;
+ fd->is_func_expr = is_func_expr;
+ js_dbuf_init(ctx, &fd->byte_code);
+ fd->last_opcode_pos = -1;
+ fd->func_name = JS_ATOM_NULL;
+ fd->var_object_idx = -1;
+ fd->arg_var_object_idx = -1;
+ fd->arguments_var_idx = -1;
+ fd->arguments_arg_idx = -1;
+ fd->func_var_idx = -1;
+ fd->eval_ret_idx = -1;
+ fd->this_var_idx = -1;
+ fd->new_target_var_idx = -1;
+ fd->this_active_func_var_idx = -1;
+ fd->home_object_var_idx = -1;
+
+ /* XXX: should distinguish arg, var and var object and body scopes */
+ fd->scopes = fd->def_scope_array;
+ fd->scope_size = countof(fd->def_scope_array);
+ fd->scope_count = 1;
+ fd->scopes[0].first = -1;
+ fd->scopes[0].parent = -1;
+ fd->scope_level = 0; /* 0: var/arg scope */
+ fd->scope_first = -1;
+ fd->body_scope = -1;
+
+ fd->filename = JS_NewAtom(ctx, filename);
+ fd->line_num = line_num;
+
+ js_dbuf_init(ctx, &fd->pc2line);
+ //fd->pc2line_last_line_num = line_num;
+ //fd->pc2line_last_pc = 0;
+ fd->last_opcode_line_num = line_num;
+
+ return fd;
+}
+
+static void free_bytecode_atoms(JSRuntime *rt,
+ const uint8_t *bc_buf, int bc_len,
+ BOOL use_short_opcodes)
+{
+ int pos, len, op;
+ JSAtom atom;
+ const JSOpCode *oi;
+
+ pos = 0;
+ while (pos < bc_len) {
+ op = bc_buf[pos];
+ if (use_short_opcodes)
+ oi = &short_opcode_info(op);
+ else
+ oi = &opcode_info[op];
+
+ len = oi->size;
+ switch(oi->fmt) {
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ case OP_FMT_atom_u16:
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ atom = get_u32(bc_buf + pos + 1);
+ JS_FreeAtomRT(rt, atom);
+ break;
+ default:
+ break;
+ }
+ pos += len;
+ }
+}
+
+static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
+{
+ int i;
+ struct list_head *el, *el1;
+
+ /* free the child functions */
+ list_for_each_safe(el, el1, &fd->child_list) {
+ JSFunctionDef *fd1;
+ fd1 = list_entry(el, JSFunctionDef, link);
+ js_free_function_def(ctx, fd1);
+ }
+
+ free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,
+ fd->use_short_opcodes);
+ dbuf_free(&fd->byte_code);
+ js_free(ctx, fd->jump_slots);
+ js_free(ctx, fd->label_slots);
+ js_free(ctx, fd->line_number_slots);
+
+ for(i = 0; i < fd->cpool_count; i++) {
+ JS_FreeValue(ctx, fd->cpool[i]);
+ }
+ js_free(ctx, fd->cpool);
+
+ JS_FreeAtom(ctx, fd->func_name);
+
+ for(i = 0; i < fd->var_count; i++) {
+ JS_FreeAtom(ctx, fd->vars[i].var_name);
+ }
+ js_free(ctx, fd->vars);
+ for(i = 0; i < fd->arg_count; i++) {
+ JS_FreeAtom(ctx, fd->args[i].var_name);
+ }
+ js_free(ctx, fd->args);
+
+ for(i = 0; i < fd->global_var_count; i++) {
+ JS_FreeAtom(ctx, fd->global_vars[i].var_name);
+ }
+ js_free(ctx, fd->global_vars);
+
+ for(i = 0; i < fd->closure_var_count; i++) {
+ JSClosureVar *cv = &fd->closure_var[i];
+ JS_FreeAtom(ctx, cv->var_name);
+ }
+ js_free(ctx, fd->closure_var);
+
+ if (fd->scopes != fd->def_scope_array)
+ js_free(ctx, fd->scopes);
+
+ JS_FreeAtom(ctx, fd->filename);
+ dbuf_free(&fd->pc2line);
+
+ js_free(ctx, fd->source);
+
+ if (fd->parent) {
+ /* remove in parent list */
+ list_del(&fd->link);
+ }
+ js_free(ctx, fd);
+}
+
+#ifdef DUMP_BYTECODE
+static const char *skip_lines(const char *p, int n) {
+ while (n-- > 0 && *p) {
+ while (*p && *p++ != '\n')
+ continue;
+ }
+ return p;
+}
+
+static void print_lines(const char *source, int line, int line1) {
+ const char *s = source;
+ const char *p = skip_lines(s, line);
+ if (*p) {
+ while (line++ < line1) {
+ p = skip_lines(s = p, 1);
+ printf(";; %.*s", (int)(p - s), s);
+ if (!*p) {
+ if (p[-1] != '\n')
+ printf("\n");
+ break;
+ }
+ }
+ }
+}
+
+static void dump_byte_code(JSContext *ctx, int pass,
+ const uint8_t *tab, int len,
+ const JSVarDef *args, int arg_count,
+ const JSVarDef *vars, int var_count,
+ const JSClosureVar *closure_var, int closure_var_count,
+ const JSValue *cpool, uint32_t cpool_count,
+ const char *source, int line_num,
+ const LabelSlot *label_slots, JSFunctionBytecode *b)
+{
+ const JSOpCode *oi;
+ int pos, pos_next, op, size, idx, addr, line, line1, in_source;
+ uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));
+ BOOL use_short_opcodes = (b != NULL);
+
+ /* scan for jump targets */
+ for (pos = 0; pos < len; pos = pos_next) {
+ op = tab[pos];
+ if (use_short_opcodes)
+ oi = &short_opcode_info(op);
+ else
+ oi = &opcode_info[op];
+ pos_next = pos + oi->size;
+ if (op < OP_COUNT) {
+ switch (oi->fmt) {
+#if SHORT_OPCODES
+ case OP_FMT_label8:
+ pos++;
+ addr = (int8_t)tab[pos];
+ goto has_addr;
+ case OP_FMT_label16:
+ pos++;
+ addr = (int16_t)get_u16(tab + pos);
+ goto has_addr;
+#endif
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ pos += 4;
+ /* fall thru */
+ case OP_FMT_label:
+ case OP_FMT_label_u16:
+ pos++;
+ addr = get_u32(tab + pos);
+ goto has_addr;
+ has_addr:
+ if (pass == 1)
+ addr = label_slots[addr].pos;
+ if (pass == 2)
+ addr = label_slots[addr].pos2;
+ if (pass == 3)
+ addr += pos;
+ if (addr >= 0 && addr < len)
+ bits[addr] |= 1;
+ break;
+ }
+ }
+ }
+ in_source = 0;
+ if (source) {
+ /* Always print first line: needed if single line */
+ print_lines(source, 0, 1);
+ in_source = 1;
+ }
+ line1 = line = 1;
+ pos = 0;
+ while (pos < len) {
+ op = tab[pos];
+ if (source) {
+ if (b) {
+ line1 = find_line_num(ctx, b, pos) - line_num + 1;
+ } else if (op == OP_line_num) {
+ line1 = get_u32(tab + pos + 1) - line_num + 1;
+ }
+ if (line1 > line) {
+ if (!in_source)
+ printf("\n");
+ in_source = 1;
+ print_lines(source, line, line1);
+ line = line1;
+ //bits[pos] |= 2;
+ }
+ }
+ if (in_source)
+ printf("\n");
+ in_source = 0;
+ if (op >= OP_COUNT) {
+ printf("invalid opcode (0x%02x)\n", op);
+ pos++;
+ continue;
+ }
+ if (use_short_opcodes)
+ oi = &short_opcode_info(op);
+ else
+ oi = &opcode_info[op];
+ size = oi->size;
+ if (pos + size > len) {
+ printf("truncated opcode (0x%02x)\n", op);
+ break;
+ }
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)
+ {
+ int i, x, x0;
+ x = x0 = printf("%5d ", pos);
+ for (i = 0; i < size; i++) {
+ if (i == 6) {
+ printf("\n%*s", x = x0, "");
+ }
+ x += printf(" %02X", tab[pos + i]);
+ }
+ printf("%*s", x0 + 20 - x, "");
+ }
+#endif
+ if (bits[pos]) {
+ printf("%5d: ", pos);
+ } else {
+ printf(" ");
+ }
+ printf("%s", oi->name);
+ pos++;
+ switch(oi->fmt) {
+ case OP_FMT_none_int:
+ printf(" %d", op - OP_push_0);
+ break;
+ case OP_FMT_npopx:
+ printf(" %d", op - OP_call0);
+ break;
+ case OP_FMT_u8:
+ printf(" %u", get_u8(tab + pos));
+ break;
+ case OP_FMT_i8:
+ printf(" %d", get_i8(tab + pos));
+ break;
+ case OP_FMT_u16:
+ case OP_FMT_npop:
+ printf(" %u", get_u16(tab + pos));
+ break;
+ case OP_FMT_npop_u16:
+ printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));
+ break;
+ case OP_FMT_i16:
+ printf(" %d", get_i16(tab + pos));
+ break;
+ case OP_FMT_i32:
+ printf(" %d", get_i32(tab + pos));
+ break;
+ case OP_FMT_u32:
+ printf(" %u", get_u32(tab + pos));
+ break;
+#if SHORT_OPCODES
+ case OP_FMT_label8:
+ addr = get_i8(tab + pos);
+ goto has_addr1;
+ case OP_FMT_label16:
+ addr = get_i16(tab + pos);
+ goto has_addr1;
+#endif
+ case OP_FMT_label:
+ addr = get_u32(tab + pos);
+ goto has_addr1;
+ has_addr1:
+ if (pass == 1)
+ printf(" %u:%u", addr, label_slots[addr].pos);
+ if (pass == 2)
+ printf(" %u:%u", addr, label_slots[addr].pos2);
+ if (pass == 3)
+ printf(" %u", addr + pos);
+ break;
+ case OP_FMT_label_u16:
+ addr = get_u32(tab + pos);
+ if (pass == 1)
+ printf(" %u:%u", addr, label_slots[addr].pos);
+ if (pass == 2)
+ printf(" %u:%u", addr, label_slots[addr].pos2);
+ if (pass == 3)
+ printf(" %u", addr + pos);
+ printf(",%u", get_u16(tab + pos + 4));
+ break;
+#if SHORT_OPCODES
+ case OP_FMT_const8:
+ idx = get_u8(tab + pos);
+ goto has_pool_idx;
+#endif
+ case OP_FMT_const:
+ idx = get_u32(tab + pos);
+ goto has_pool_idx;
+ has_pool_idx:
+ printf(" %u: ", idx);
+ if (idx < cpool_count) {
+ JS_DumpValue(ctx, cpool[idx]);
+ }
+ break;
+ case OP_FMT_atom:
+ printf(" ");
+ print_atom(ctx, get_u32(tab + pos));
+ break;
+ case OP_FMT_atom_u8:
+ printf(" ");
+ print_atom(ctx, get_u32(tab + pos));
+ printf(",%d", get_u8(tab + pos + 4));
+ break;
+ case OP_FMT_atom_u16:
+ printf(" ");
+ print_atom(ctx, get_u32(tab + pos));
+ printf(",%d", get_u16(tab + pos + 4));
+ break;
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ printf(" ");
+ print_atom(ctx, get_u32(tab + pos));
+ addr = get_u32(tab + pos + 4);
+ if (pass == 1)
+ printf(",%u:%u", addr, label_slots[addr].pos);
+ if (pass == 2)
+ printf(",%u:%u", addr, label_slots[addr].pos2);
+ if (pass == 3)
+ printf(",%u", addr + pos + 4);
+ if (oi->fmt == OP_FMT_atom_label_u8)
+ printf(",%u", get_u8(tab + pos + 8));
+ else
+ printf(",%u", get_u16(tab + pos + 8));
+ break;
+ case OP_FMT_none_loc:
+ idx = (op - OP_get_loc0) % 4;
+ goto has_loc;
+ case OP_FMT_loc8:
+ idx = get_u8(tab + pos);
+ goto has_loc;
+ case OP_FMT_loc:
+ idx = get_u16(tab + pos);
+ has_loc:
+ printf(" %d: ", idx);
+ if (idx < var_count) {
+ print_atom(ctx, vars[idx].var_name);
+ }
+ break;
+ case OP_FMT_none_arg:
+ idx = (op - OP_get_arg0) % 4;
+ goto has_arg;
+ case OP_FMT_arg:
+ idx = get_u16(tab + pos);
+ has_arg:
+ printf(" %d: ", idx);
+ if (idx < arg_count) {
+ print_atom(ctx, args[idx].var_name);
+ }
+ break;
+ case OP_FMT_none_var_ref:
+ idx = (op - OP_get_var_ref0) % 4;
+ goto has_var_ref;
+ case OP_FMT_var_ref:
+ idx = get_u16(tab + pos);
+ has_var_ref:
+ printf(" %d: ", idx);
+ if (idx < closure_var_count) {
+ print_atom(ctx, closure_var[idx].var_name);
+ }
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ pos += oi->size - 1;
+ }
+ if (source) {
+ if (!in_source)
+ printf("\n");
+ print_lines(source, line, INT32_MAX);
+ }
+ js_free(ctx, bits);
+}
+
+static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len,
+ int line_num)
+{
+ const uint8_t *p_end, *p_next, *p;
+ int pc, v;
+ unsigned int op;
+
+ if (len <= 0)
+ return;
+
+ printf("%5s %5s\n", "PC", "LINE");
+
+ p = buf;
+ p_end = buf + len;
+ pc = 0;
+ while (p < p_end) {
+ op = *p++;
+ if (op == 0) {
+ v = unicode_from_utf8(p, p_end - p, &p_next);
+ if (v < 0)
+ goto fail;
+ pc += v;
+ p = p_next;
+ v = unicode_from_utf8(p, p_end - p, &p_next);
+ if (v < 0) {
+ fail:
+ printf("invalid pc2line encode pos=%d\n", (int)(p - buf));
+ return;
+ }
+ if (!(v & 1)) {
+ v = v >> 1;
+ } else {
+ v = -(v >> 1) - 1;
+ }
+ line_num += v;
+ p = p_next;
+ } else {
+ op -= PC2LINE_OP_FIRST;
+ pc += (op / PC2LINE_RANGE);
+ line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;
+ }
+ printf("%5d %5d\n", pc, line_num);
+ }
+}
+
+static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)
+{
+ int i;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ const char *str;
+
+ if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
+ str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);
+ printf("%s:%d: ", str, b->debug.line_num);
+ }
+
+ str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);
+ printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);
+ if (b->js_mode) {
+ printf(" mode:");
+ if (b->js_mode & JS_MODE_STRICT)
+ printf(" strict");
+#ifdef CONFIG_BIGNUM
+ if (b->js_mode & JS_MODE_MATH)
+ printf(" math");
+#endif
+ printf("\n");
+ }
+ if (b->arg_count && b->vardefs) {
+ printf(" args:");
+ for(i = 0; i < b->arg_count; i++) {
+ printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
+ b->vardefs[i].var_name));
+ }
+ printf("\n");
+ }
+ if (b->var_count && b->vardefs) {
+ printf(" locals:\n");
+ for(i = 0; i < b->var_count; i++) {
+ JSVarDef *vd = &b->vardefs[b->arg_count + i];
+ printf("%5d: %s %s", i,
+ vd->var_kind == JS_VAR_CATCH ? "catch" :
+ (vd->var_kind == JS_VAR_FUNCTION_DECL ||
+ vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :
+ vd->is_const ? "const" :
+ vd->is_lexical ? "let" : "var",
+ JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
+ if (vd->scope_level)
+ printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);
+ printf("\n");
+ }
+ }
+ if (b->closure_var_count) {
+ printf(" closure vars:\n");
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ printf("%5d: %s %s:%s%d %s\n", i,
+ JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),
+ cv->is_local ? "local" : "parent",
+ cv->is_arg ? "arg" : "loc", cv->var_idx,
+ cv->is_const ? "const" :
+ cv->is_lexical ? "let" : "var");
+ }
+ }
+ printf(" stack_size: %d\n", b->stack_size);
+ printf(" opcodes:\n");
+ dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
+ b->vardefs, b->arg_count,
+ b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,
+ b->closure_var, b->closure_var_count,
+ b->cpool, b->cpool_count,
+ b->has_debug ? b->debug.source : NULL,
+ b->has_debug ? b->debug.line_num : -1, NULL, b);
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)
+ if (b->has_debug)
+ dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num);
+#endif
+ printf("\n");
+}
+#endif
+
+static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
+ BOOL is_local, BOOL is_arg,
+ int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind)
+{
+ JSClosureVar *cv;
+
+ /* the closure variable indexes are currently stored on 16 bits */
+ if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {
+ JS_ThrowInternalError(ctx, "too many closure variables");
+ return -1;
+ }
+
+ if (js_resize_array(ctx, (void **)&s->closure_var,
+ sizeof(s->closure_var[0]),
+ &s->closure_var_size, s->closure_var_count + 1))
+ return -1;
+ cv = &s->closure_var[s->closure_var_count++];
+ cv->is_local = is_local;
+ cv->is_arg = is_arg;
+ cv->is_const = is_const;
+ cv->is_lexical = is_lexical;
+ cv->var_kind = var_kind;
+ cv->var_idx = var_idx;
+ cv->var_name = JS_DupAtom(ctx, var_name);
+ return s->closure_var_count - 1;
+}
+
+static int find_closure_var(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name)
+{
+ int i;
+ for(i = 0; i < s->closure_var_count; i++) {
+ JSClosureVar *cv = &s->closure_var[i];
+ if (cv->var_name == var_name)
+ return i;
+ }
+ return -1;
+}
+
+/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a
+ local variable (is_local = TRUE) or a closure (is_local = FALSE) in
+ 'fd' */
+static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionDef *fd, BOOL is_local,
+ BOOL is_arg, int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind)
+{
+ int i;
+
+ if (fd != s->parent) {
+ var_idx = get_closure_var2(ctx, s->parent, fd, is_local,
+ is_arg, var_idx, var_name,
+ is_const, is_lexical, var_kind);
+ if (var_idx < 0)
+ return -1;
+ is_local = FALSE;
+ }
+ for(i = 0; i < s->closure_var_count; i++) {
+ JSClosureVar *cv = &s->closure_var[i];
+ if (cv->var_idx == var_idx && cv->is_arg == is_arg &&
+ cv->is_local == is_local)
+ return i;
+ }
+ return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,
+ is_const, is_lexical, var_kind);
+}
+
+static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionDef *fd, BOOL is_arg,
+ int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind)
+{
+ return get_closure_var2(ctx, s, fd, TRUE, is_arg,
+ var_idx, var_name, is_const, is_lexical,
+ var_kind);
+}
+
+static int get_with_scope_opcode(int op)
+{
+ if (op == OP_scope_get_var_undef)
+ return OP_with_get_var;
+ else
+ return OP_with_get_var + (op - OP_scope_get_var);
+}
+
+static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)
+{
+ int opcode = bc_buf[pos];
+ return (bc_buf[pos + 1] == OP_put_ref_value &&
+ (opcode == OP_insert3 ||
+ opcode == OP_perm4 ||
+ opcode == OP_nop ||
+ opcode == OP_rot3l));
+}
+
+static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)
+{
+ int opcode = bc_buf[pos];
+ return (bc_buf[pos + 1] == OP_put_ref_value &&
+ (opcode == OP_insert3 ||
+ opcode == OP_perm4 ||
+ opcode == OP_nop ||
+ opcode == OP_rot3l));
+}
+
+static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,
+ DynBuf *bc, uint8_t *bc_buf,
+ LabelSlot *ls, int pos_next,
+ int get_op, int var_idx)
+{
+ int label_pos, end_pos, pos;
+
+ /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`
+ but only if expr does not modify `a`.
+ should scan the code between pos_next and label_pos
+ for operations that can potentially change `a`:
+ OP_scope_make_ref(a), function calls, jumps and gosub.
+ */
+ /* replace the reference get/put with normal variable
+ accesses */
+ if (bc_buf[pos_next] == OP_get_ref_value) {
+ dbuf_putc(bc, get_op);
+ dbuf_put_u16(bc, var_idx);
+ pos_next++;
+ }
+ /* remove the OP_label to make room for replacement */
+ /* label should have a refcount of 0 anyway */
+ /* XXX: should avoid this patch by inserting nops in phase 1 */
+ label_pos = ls->pos;
+ pos = label_pos - 5;
+ assert(bc_buf[pos] == OP_label);
+ /* label points to an instruction pair:
+ - insert3 / put_ref_value
+ - perm4 / put_ref_value
+ - rot3l / put_ref_value
+ - nop / put_ref_value
+ */
+ end_pos = label_pos + 2;
+ if (bc_buf[label_pos] == OP_insert3)
+ bc_buf[pos++] = OP_dup;
+ bc_buf[pos] = get_op + 1;
+ put_u16(bc_buf + pos + 1, var_idx);
+ pos += 3;
+ /* pad with OP_nop */
+ while (pos < end_pos)
+ bc_buf[pos++] = OP_nop;
+ return pos_next;
+}
+
+static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
+ DynBuf *bc, uint8_t *bc_buf,
+ LabelSlot *ls, int pos_next,
+ JSAtom var_name)
+{
+ int label_pos, end_pos, pos, op;
+ BOOL is_strict;
+ is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);
+
+ /* replace the reference get/put with normal variable
+ accesses */
+ if (is_strict) {
+ /* need to check if the variable exists before evaluating the right
+ expression */
+ /* XXX: need an extra OP_true if destructuring an array */
+ dbuf_putc(bc, OP_check_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ } else {
+ /* XXX: need 2 extra OP_true if destructuring an array */
+ }
+ if (bc_buf[pos_next] == OP_get_ref_value) {
+ dbuf_putc(bc, OP_get_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ pos_next++;
+ }
+ /* remove the OP_label to make room for replacement */
+ /* label should have a refcount of 0 anyway */
+ /* XXX: should have emitted several OP_nop to avoid this kludge */
+ label_pos = ls->pos;
+ pos = label_pos - 5;
+ assert(bc_buf[pos] == OP_label);
+ end_pos = label_pos + 2;
+ op = bc_buf[label_pos];
+ if (is_strict) {
+ if (op != OP_nop) {
+ switch(op) {
+ case OP_insert3:
+ op = OP_insert2;
+ break;
+ case OP_perm4:
+ op = OP_perm3;
+ break;
+ case OP_rot3l:
+ op = OP_swap;
+ break;
+ default:
+ abort();
+ }
+ bc_buf[pos++] = op;
+ }
+ } else {
+ if (op == OP_insert3)
+ bc_buf[pos++] = OP_dup;
+ }
+ if (is_strict) {
+ bc_buf[pos] = OP_put_var_strict;
+ /* XXX: need 1 extra OP_drop if destructuring an array */
+ } else {
+ bc_buf[pos] = OP_put_var;
+ /* XXX: need 2 extra OP_drop if destructuring an array */
+ }
+ put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
+ pos += 5;
+ /* pad with OP_nop */
+ while (pos < end_pos)
+ bc_buf[pos++] = OP_nop;
+ return pos_next;
+}
+
+static int add_var_this(JSContext *ctx, JSFunctionDef *fd)
+{
+ int idx;
+ idx = add_var(ctx, fd, JS_ATOM_this);
+ if (idx >= 0 && fd->is_derived_class_constructor) {
+ JSVarDef *vd = &fd->vars[idx];
+ /* XXX: should have is_this flag or var type */
+ vd->is_lexical = 1; /* used to trigger 'uninitialized' checks
+ in a derived class constructor */
+ }
+ return idx;
+}
+
+static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name)
+{
+ int var_idx;
+
+ if (!s->has_this_binding)
+ return -1;
+ switch(var_name) {
+ case JS_ATOM_home_object:
+ /* 'home_object' pseudo variable */
+ if (s->home_object_var_idx < 0)
+ s->home_object_var_idx = add_var(ctx, s, var_name);
+ var_idx = s->home_object_var_idx;
+ break;
+ case JS_ATOM_this_active_func:
+ /* 'this.active_func' pseudo variable */
+ if (s->this_active_func_var_idx < 0)
+ s->this_active_func_var_idx = add_var(ctx, s, var_name);
+ var_idx = s->this_active_func_var_idx;
+ break;
+ case JS_ATOM_new_target:
+ /* 'new.target' pseudo variable */
+ if (s->new_target_var_idx < 0)
+ s->new_target_var_idx = add_var(ctx, s, var_name);
+ var_idx = s->new_target_var_idx;
+ break;
+ case JS_ATOM_this:
+ /* 'this' pseudo variable */
+ if (s->this_var_idx < 0)
+ s->this_var_idx = add_var_this(ctx, s);
+ var_idx = s->this_var_idx;
+ break;
+ default:
+ var_idx = -1;
+ break;
+ }
+ return var_idx;
+}
+
+/* test if 'var_name' is in the variable object on the stack. If is it
+ the case, handle it and jump to 'label_done' */
+static void var_object_test(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name, int op, DynBuf *bc,
+ int *plabel_done, BOOL is_with)
+{
+ dbuf_putc(bc, get_with_scope_opcode(op));
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ *plabel_done = new_label_fd(s, *plabel_done);
+ dbuf_put_u32(bc, *plabel_done);
+ dbuf_putc(bc, is_with);
+ update_label(s, *plabel_done, 1);
+ s->jump_size++;
+}
+
+/* return the position of the next opcode */
+static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name, int scope_level, int op,
+ DynBuf *bc, uint8_t *bc_buf,
+ LabelSlot *ls, int pos_next)
+{
+ int idx, var_idx, is_put;
+ int label_done;
+ JSFunctionDef *fd;
+ JSVarDef *vd;
+ BOOL is_pseudo_var, is_arg_scope;
+
+ label_done = -1;
+
+ /* XXX: could be simpler to use a specific function to
+ resolve the pseudo variables */
+ is_pseudo_var = (var_name == JS_ATOM_home_object ||
+ var_name == JS_ATOM_this_active_func ||
+ var_name == JS_ATOM_new_target ||
+ var_name == JS_ATOM_this);
+
+ /* resolve local scoped variables */
+ var_idx = -1;
+ for (idx = s->scopes[scope_level].first; idx >= 0;) {
+ vd = &s->vars[idx];
+ if (vd->var_name == var_name) {
+ if (op == OP_scope_put_var || op == OP_scope_make_ref) {
+ if (vd->is_const) {
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ goto done;
+ }
+ }
+ var_idx = idx;
+ break;
+ } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
+ }
+ idx = vd->scope_next;
+ }
+ is_arg_scope = (idx == ARG_SCOPE_END);
+ if (var_idx < 0) {
+ /* argument scope: variables are not visible but pseudo
+ variables are visible */
+ if (!is_arg_scope) {
+ var_idx = find_var(ctx, s, var_name);
+ }
+
+ if (var_idx < 0 && is_pseudo_var)
+ var_idx = resolve_pseudo_var(ctx, s, var_name);
+
+ if (var_idx < 0 && var_name == JS_ATOM_arguments &&
+ s->has_arguments_binding) {
+ /* 'arguments' pseudo variable */
+ var_idx = add_arguments_var(ctx, s);
+ }
+ if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {
+ /* add a new variable with the function name */
+ var_idx = add_func_var(ctx, s, var_name);
+ }
+ }
+ if (var_idx >= 0) {
+ if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
+ !(var_idx & ARGUMENT_VAR_OFFSET) &&
+ s->vars[var_idx].is_const) {
+ /* only happens when assigning a function expression name
+ in strict mode */
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ goto done;
+ }
+ /* OP_scope_put_var_init is only used to initialize a
+ lexical variable, so it is never used in a with or var object. It
+ can be used with a closure (module global variable case). */
+ switch (op) {
+ case OP_scope_make_ref:
+ if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
+ s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
+ /* Create a dummy object reference for the func_var */
+ dbuf_putc(bc, OP_object);
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, var_idx);
+ dbuf_putc(bc, OP_define_field);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, OP_push_atom_value);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ } else
+ if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {
+ int get_op;
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ get_op = OP_get_arg;
+ var_idx -= ARGUMENT_VAR_OFFSET;
+ } else {
+ if (s->vars[var_idx].is_lexical)
+ get_op = OP_get_loc_check;
+ else
+ get_op = OP_get_loc;
+ }
+ pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
+ pos_next, get_op, var_idx);
+ } else {
+ /* Create a dummy object with a named slot that is
+ a reference to the local variable */
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ dbuf_putc(bc, OP_make_arg_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
+ } else {
+ dbuf_putc(bc, OP_make_loc_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_put_u16(bc, var_idx);
+ }
+ }
+ break;
+ case OP_scope_get_ref:
+ dbuf_putc(bc, OP_undefined);
+ /* fall thru */
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ case OP_scope_put_var_init:
+ is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ dbuf_putc(bc, OP_get_arg + is_put);
+ dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
+ } else {
+ if (is_put) {
+ if (s->vars[var_idx].is_lexical) {
+ if (op == OP_scope_put_var_init) {
+ /* 'this' can only be initialized once */
+ if (var_name == JS_ATOM_this)
+ dbuf_putc(bc, OP_put_loc_check_init);
+ else
+ dbuf_putc(bc, OP_put_loc);
+ } else {
+ dbuf_putc(bc, OP_put_loc_check);
+ }
+ } else {
+ dbuf_putc(bc, OP_put_loc);
+ }
+ } else {
+ if (s->vars[var_idx].is_lexical) {
+ dbuf_putc(bc, OP_get_loc_check);
+ } else {
+ dbuf_putc(bc, OP_get_loc);
+ }
+ }
+ dbuf_put_u16(bc, var_idx);
+ }
+ break;
+ case OP_scope_delete_var:
+ dbuf_putc(bc, OP_push_false);
+ break;
+ }
+ goto done;
+ }
+ /* check eval object */
+ if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) {
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, s->var_object_idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
+ }
+ /* check eval object in argument scope */
+ if (s->arg_var_object_idx >= 0 && !is_pseudo_var) {
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, s->arg_var_object_idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
+ }
+
+ /* check parent scopes */
+ for (fd = s; fd->parent;) {
+ scope_level = fd->parent_scope_level;
+ fd = fd->parent;
+ for (idx = fd->scopes[scope_level].first; idx >= 0;) {
+ vd = &fd->vars[idx];
+ if (vd->var_name == var_name) {
+ if (op == OP_scope_put_var || op == OP_scope_make_ref) {
+ if (vd->is_const) {
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ goto done;
+ }
+ }
+ var_idx = idx;
+ break;
+ } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
+ vd->is_captured = 1;
+ idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);
+ if (idx >= 0) {
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
+ }
+ }
+ idx = vd->scope_next;
+ }
+ is_arg_scope = (idx == ARG_SCOPE_END);
+ if (var_idx >= 0)
+ break;
+
+ if (!is_arg_scope) {
+ var_idx = find_var(ctx, fd, var_name);
+ if (var_idx >= 0)
+ break;
+ }
+ if (is_pseudo_var) {
+ var_idx = resolve_pseudo_var(ctx, fd, var_name);
+ if (var_idx >= 0)
+ break;
+ }
+ if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {
+ var_idx = add_arguments_var(ctx, fd);
+ break;
+ }
+ if (fd->is_func_expr && fd->func_name == var_name) {
+ /* add a new variable with the function name */
+ var_idx = add_func_var(ctx, fd, var_name);
+ break;
+ }
+
+ /* check eval object */
+ if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
+ vd = &fd->vars[fd->var_object_idx];
+ vd->is_captured = 1;
+ idx = get_closure_var(ctx, s, fd, FALSE,
+ fd->var_object_idx, vd->var_name,
+ FALSE, FALSE, JS_VAR_NORMAL);
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
+ }
+
+ /* check eval object in argument scope */
+ if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
+ vd = &fd->vars[fd->arg_var_object_idx];
+ vd->is_captured = 1;
+ idx = get_closure_var(ctx, s, fd, FALSE,
+ fd->arg_var_object_idx, vd->var_name,
+ FALSE, FALSE, JS_VAR_NORMAL);
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
+ }
+
+ if (fd->is_eval)
+ break; /* it it necessarily the top level function */
+ }
+
+ /* check direct eval scope (in the closure of the eval function
+ which is necessarily at the top level) */
+ if (!fd)
+ fd = s;
+ if (var_idx < 0 && fd->is_eval) {
+ int idx1;
+ for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {
+ JSClosureVar *cv = &fd->closure_var[idx1];
+ if (var_name == cv->var_name) {
+ if (fd != s) {
+ idx = get_closure_var2(ctx, s, fd,
+ FALSE,
+ cv->is_arg, idx1,
+ cv->var_name, cv->is_const,
+ cv->is_lexical, cv->var_kind);
+ } else {
+ idx = idx1;
+ }
+ goto has_idx;
+ } else if ((cv->var_name == JS_ATOM__var_ ||
+ cv->var_name == JS_ATOM__arg_var_ ||
+ cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {
+ int is_with = (cv->var_name == JS_ATOM__with_);
+ if (fd != s) {
+ idx = get_closure_var2(ctx, s, fd,
+ FALSE,
+ cv->is_arg, idx1,
+ cv->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ } else {
+ idx = idx1;
+ }
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, is_with);
+ }
+ }
+ }
+
+ if (var_idx >= 0) {
+ /* find the corresponding closure variable */
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;
+ idx = get_closure_var(ctx, s, fd,
+ TRUE, var_idx - ARGUMENT_VAR_OFFSET,
+ var_name, FALSE, FALSE, JS_VAR_NORMAL);
+ } else {
+ fd->vars[var_idx].is_captured = 1;
+ idx = get_closure_var(ctx, s, fd,
+ FALSE, var_idx,
+ var_name,
+ fd->vars[var_idx].is_const,
+ fd->vars[var_idx].is_lexical,
+ fd->vars[var_idx].var_kind);
+ }
+ if (idx >= 0) {
+ has_idx:
+ if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
+ s->closure_var[idx].is_const) {
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ goto done;
+ }
+ switch (op) {
+ case OP_scope_make_ref:
+ if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
+ /* Create a dummy object reference for the func_var */
+ dbuf_putc(bc, OP_object);
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ dbuf_putc(bc, OP_define_field);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, OP_push_atom_value);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ } else
+ if (label_done == -1 &&
+ can_opt_put_ref_value(bc_buf, ls->pos)) {
+ int get_op;
+ if (s->closure_var[idx].is_lexical)
+ get_op = OP_get_var_ref_check;
+ else
+ get_op = OP_get_var_ref;
+ pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
+ pos_next,
+ get_op, idx);
+ } else {
+ /* Create a dummy object with a named slot that is
+ a reference to the closure variable */
+ dbuf_putc(bc, OP_make_var_ref_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_put_u16(bc, idx);
+ }
+ break;
+ case OP_scope_get_ref:
+ /* XXX: should create a dummy object with a named slot that is
+ a reference to the closure variable */
+ dbuf_putc(bc, OP_undefined);
+ /* fall thru */
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ case OP_scope_put_var_init:
+ is_put = (op == OP_scope_put_var ||
+ op == OP_scope_put_var_init);
+ if (is_put) {
+ if (s->closure_var[idx].is_lexical) {
+ if (op == OP_scope_put_var_init) {
+ /* 'this' can only be initialized once */
+ if (var_name == JS_ATOM_this)
+ dbuf_putc(bc, OP_put_var_ref_check_init);
+ else
+ dbuf_putc(bc, OP_put_var_ref);
+ } else {
+ dbuf_putc(bc, OP_put_var_ref_check);
+ }
+ } else {
+ dbuf_putc(bc, OP_put_var_ref);
+ }
+ } else {
+ if (s->closure_var[idx].is_lexical) {
+ dbuf_putc(bc, OP_get_var_ref_check);
+ } else {
+ dbuf_putc(bc, OP_get_var_ref);
+ }
+ }
+ dbuf_put_u16(bc, idx);
+ break;
+ case OP_scope_delete_var:
+ dbuf_putc(bc, OP_push_false);
+ break;
+ }
+ goto done;
+ }
+ }
+
+ /* global variable access */
+
+ switch (op) {
+ case OP_scope_make_ref:
+ if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {
+ pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,
+ pos_next, var_name);
+ } else {
+ dbuf_putc(bc, OP_make_var_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ }
+ break;
+ case OP_scope_get_ref:
+ /* XXX: should create a dummy object with a named slot that is
+ a reference to the global variable */
+ dbuf_putc(bc, OP_undefined);
+ dbuf_putc(bc, OP_get_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ case OP_scope_put_var_init:
+ dbuf_putc(bc, OP_put_var_init);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ case OP_scope_delete_var:
+ dbuf_putc(bc, OP_delete_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ }
+done:
+ if (label_done >= 0) {
+ dbuf_putc(bc, OP_label);
+ dbuf_put_u32(bc, label_done);
+ s->label_slots[label_done].pos2 = bc->size;
+ }
+ return pos_next;
+}
+
+/* search in all scopes */
+static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,
+ JSAtom name, int scope_level)
+{
+ int idx;
+
+ idx = fd->scopes[scope_level].first;
+ while (idx >= 0) {
+ if (fd->vars[idx].var_name == name)
+ return idx;
+ idx = fd->vars[idx].scope_next;
+ }
+ return -1;
+}
+
+static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)
+{
+ /* if the field is not initialized, the error is catched when
+ accessing it */
+ if (is_ref)
+ dbuf_putc(bc, OP_get_var_ref);
+ else
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, idx);
+}
+
+static int resolve_scope_private_field1(JSContext *ctx,
+ BOOL *pis_ref, int *pvar_kind,
+ JSFunctionDef *s,
+ JSAtom var_name, int scope_level)
+{
+ int idx, var_kind;
+ JSFunctionDef *fd;
+ BOOL is_ref;
+
+ fd = s;
+ is_ref = FALSE;
+ for(;;) {
+ idx = find_private_class_field_all(ctx, fd, var_name, scope_level);
+ if (idx >= 0) {
+ var_kind = fd->vars[idx].var_kind;
+ if (is_ref) {
+ idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,
+ TRUE, TRUE, JS_VAR_NORMAL);
+ if (idx < 0)
+ return -1;
+ }
+ break;
+ }
+ scope_level = fd->parent_scope_level;
+ if (!fd->parent) {
+ if (fd->is_eval) {
+ /* closure of the eval function (top level) */
+ for (idx = 0; idx < fd->closure_var_count; idx++) {
+ JSClosureVar *cv = &fd->closure_var[idx];
+ if (cv->var_name == var_name) {
+ var_kind = cv->var_kind;
+ is_ref = TRUE;
+ if (fd != s) {
+ idx = get_closure_var2(ctx, s, fd,
+ FALSE,
+ cv->is_arg, idx,
+ cv->var_name, cv->is_const,
+ cv->is_lexical,
+ cv->var_kind);
+ if (idx < 0)
+ return -1;
+ }
+ goto done;
+ }
+ }
+ }
+ /* XXX: no line number info */
+ JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",
+ var_name);
+ return -1;
+ } else {
+ fd = fd->parent;
+ }
+ is_ref = TRUE;
+ }
+ done:
+ *pis_ref = is_ref;
+ *pvar_kind = var_kind;
+ return idx;
+}
+
+/* return 0 if OK or -1 if the private field could not be resolved */
+static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name, int scope_level, int op,
+ DynBuf *bc)
+{
+ int idx, var_kind;
+ BOOL is_ref;
+
+ idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,
+ var_name, scope_level);
+ if (idx < 0)
+ return -1;
+ assert(var_kind != JS_VAR_NORMAL);
+ switch (op) {
+ case OP_scope_get_private_field:
+ case OP_scope_get_private_field2:
+ switch(var_kind) {
+ case JS_VAR_PRIVATE_FIELD:
+ if (op == OP_scope_get_private_field2)
+ dbuf_putc(bc, OP_dup);
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_get_private_field);
+ break;
+ case JS_VAR_PRIVATE_METHOD:
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_check_brand);
+ if (op != OP_scope_get_private_field2)
+ dbuf_putc(bc, OP_nip);
+ break;
+ case JS_VAR_PRIVATE_GETTER:
+ case JS_VAR_PRIVATE_GETTER_SETTER:
+ if (op == OP_scope_get_private_field2)
+ dbuf_putc(bc, OP_dup);
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_check_brand);
+ dbuf_putc(bc, OP_call_method);
+ dbuf_put_u16(bc, 0);
+ break;
+ case JS_VAR_PRIVATE_SETTER:
+ /* XXX: add clearer error message */
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case OP_scope_put_private_field:
+ switch(var_kind) {
+ case JS_VAR_PRIVATE_FIELD:
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_put_private_field);
+ break;
+ case JS_VAR_PRIVATE_METHOD:
+ case JS_VAR_PRIVATE_GETTER:
+ /* XXX: add clearer error message */
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ break;
+ case JS_VAR_PRIVATE_SETTER:
+ case JS_VAR_PRIVATE_GETTER_SETTER:
+ {
+ JSAtom setter_name = get_private_setter_name(ctx, var_name);
+ if (setter_name == JS_ATOM_NULL)
+ return -1;
+ idx = resolve_scope_private_field1(ctx, &is_ref,
+ &var_kind, s,
+ setter_name, scope_level);
+ JS_FreeAtom(ctx, setter_name);
+ if (idx < 0)
+ return -1;
+ assert(var_kind == JS_VAR_PRIVATE_SETTER);
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_swap);
+ /* obj func value */
+ dbuf_putc(bc, OP_rot3r);
+ /* value obj func */
+ dbuf_putc(bc, OP_check_brand);
+ dbuf_putc(bc, OP_rot3l);
+ /* obj func value */
+ dbuf_putc(bc, OP_call_method);
+ dbuf_put_u16(bc, 1);
+ }
+ break;
+ default:
+ abort();
+ }
+ break;
+ default:
+ abort();
+ }
+ return 0;
+}
+
+static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,
+ int scope_level)
+{
+ int idx;
+ JSVarDef *vd;
+
+ for (idx = s->scopes[scope_level].first; idx >= 0;) {
+ vd = &s->vars[idx];
+ vd->is_captured = 1;
+ idx = vd->scope_next;
+ }
+}
+
+/* XXX: should handle the argument scope generically */
+static BOOL is_var_in_arg_scope(const JSVarDef *vd)
+{
+ return (vd->var_name == JS_ATOM_home_object ||
+ vd->var_name == JS_ATOM_this_active_func ||
+ vd->var_name == JS_ATOM_new_target ||
+ vd->var_name == JS_ATOM_this ||
+ vd->var_name == JS_ATOM__arg_var_ ||
+ vd->var_kind == JS_VAR_FUNCTION_NAME);
+}
+
+static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
+{
+ JSFunctionDef *fd;
+ JSVarDef *vd;
+ int i, scope_level, scope_idx;
+ BOOL has_arguments_binding, has_this_binding, is_arg_scope;
+
+ /* in non strict mode, variables are created in the caller's
+ environment object */
+ if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {
+ s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);
+ if (s->has_parameter_expressions) {
+ /* an additional variable object is needed for the
+ argument scope */
+ s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_);
+ }
+ }
+
+ /* eval can potentially use 'arguments' so we must define it */
+ has_this_binding = s->has_this_binding;
+ if (has_this_binding) {
+ if (s->this_var_idx < 0)
+ s->this_var_idx = add_var_this(ctx, s);
+ if (s->new_target_var_idx < 0)
+ s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);
+ if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)
+ s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);
+ if (s->has_home_object && s->home_object_var_idx < 0)
+ s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);
+ }
+ has_arguments_binding = s->has_arguments_binding;
+ if (has_arguments_binding) {
+ add_arguments_var(ctx, s);
+ /* also add an arguments binding in the argument scope to
+ raise an error if a direct eval in the argument scope tries
+ to redefine it */
+ if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT))
+ add_arguments_arg(ctx, s);
+ }
+ if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
+ add_func_var(ctx, s, s->func_name);
+
+ /* eval can use all the variables of the enclosing functions, so
+ they must be all put in the closure. The closure variables are
+ ordered by scope. It works only because no closure are created
+ before. */
+ assert(s->is_eval || s->closure_var_count == 0);
+
+ /* XXX: inefficient, but eval performance is less critical */
+ fd = s;
+ for(;;) {
+ scope_level = fd->parent_scope_level;
+ fd = fd->parent;
+ if (!fd)
+ break;
+ /* add 'this' if it was not previously added */
+ if (!has_this_binding && fd->has_this_binding) {
+ if (fd->this_var_idx < 0)
+ fd->this_var_idx = add_var_this(ctx, fd);
+ if (fd->new_target_var_idx < 0)
+ fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);
+ if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)
+ fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);
+ if (fd->has_home_object && fd->home_object_var_idx < 0)
+ fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);
+ has_this_binding = TRUE;
+ }
+ /* add 'arguments' if it was not previously added */
+ if (!has_arguments_binding && fd->has_arguments_binding) {
+ add_arguments_var(ctx, fd);
+ has_arguments_binding = TRUE;
+ }
+ /* add function name */
+ if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)
+ add_func_var(ctx, fd, fd->func_name);
+
+ /* add lexical variables */
+ scope_idx = fd->scopes[scope_level].first;
+ while (scope_idx >= 0) {
+ vd = &fd->vars[scope_idx];
+ vd->is_captured = 1;
+ get_closure_var(ctx, s, fd, FALSE, scope_idx,
+ vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
+ scope_idx = vd->scope_next;
+ }
+ is_arg_scope = (scope_idx == ARG_SCOPE_END);
+ if (!is_arg_scope) {
+ /* add unscoped variables */
+ for(i = 0; i < fd->arg_count; i++) {
+ vd = &fd->args[i];
+ if (vd->var_name != JS_ATOM_NULL) {
+ get_closure_var(ctx, s, fd,
+ TRUE, i, vd->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ }
+ }
+ for(i = 0; i < fd->var_count; i++) {
+ vd = &fd->vars[i];
+ /* do not close top level last result */
+ if (vd->scope_level == 0 &&
+ vd->var_name != JS_ATOM__ret_ &&
+ vd->var_name != JS_ATOM_NULL) {
+ get_closure_var(ctx, s, fd,
+ FALSE, i, vd->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ }
+ }
+ } else {
+ for(i = 0; i < fd->var_count; i++) {
+ vd = &fd->vars[i];
+ /* do not close top level last result */
+ if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
+ get_closure_var(ctx, s, fd,
+ FALSE, i, vd->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ }
+ }
+ }
+ if (fd->is_eval) {
+ int idx;
+ /* add direct eval variables (we are necessarily at the
+ top level) */
+ for (idx = 0; idx < fd->closure_var_count; idx++) {
+ JSClosureVar *cv = &fd->closure_var[idx];
+ get_closure_var2(ctx, s, fd,
+ FALSE, cv->is_arg,
+ idx, cv->var_name, cv->is_const,
+ cv->is_lexical, cv->var_kind);
+ }
+ }
+ }
+}
+
+static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
+ JSVarDef *vd, int var_idx)
+{
+ cv->is_local = TRUE;
+ cv->is_arg = FALSE;
+ cv->is_const = vd->is_const;
+ cv->is_lexical = vd->is_lexical;
+ cv->var_kind = vd->var_kind;
+ cv->var_idx = var_idx;
+ cv->var_name = JS_DupAtom(ctx, vd->var_name);
+}
+
+/* for direct eval compilation: add references to the variables of the
+ calling function */
+static warn_unused int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionBytecode *b, int scope_idx)
+{
+ int i, count;
+ JSVarDef *vd;
+ BOOL is_arg_scope;
+
+ count = b->arg_count + b->var_count + b->closure_var_count;
+ s->closure_var = NULL;
+ s->closure_var_count = 0;
+ s->closure_var_size = count;
+ if (count == 0)
+ return 0;
+ s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);
+ if (!s->closure_var)
+ return -1;
+ /* Add lexical variables in scope at the point of evaluation */
+ for (i = scope_idx; i >= 0;) {
+ vd = &b->vardefs[b->arg_count + i];
+ if (vd->scope_level > 0) {
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ set_closure_from_var(ctx, cv, vd, i);
+ }
+ i = vd->scope_next;
+ }
+ is_arg_scope = (i == ARG_SCOPE_END);
+ if (!is_arg_scope) {
+ /* Add argument variables */
+ for(i = 0; i < b->arg_count; i++) {
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ vd = &b->vardefs[i];
+ cv->is_local = TRUE;
+ cv->is_arg = TRUE;
+ cv->is_const = FALSE;
+ cv->is_lexical = FALSE;
+ cv->var_kind = JS_VAR_NORMAL;
+ cv->var_idx = i;
+ cv->var_name = JS_DupAtom(ctx, vd->var_name);
+ }
+ /* Add local non lexical variables */
+ for(i = 0; i < b->var_count; i++) {
+ vd = &b->vardefs[b->arg_count + i];
+ if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ set_closure_from_var(ctx, cv, vd, i);
+ }
+ }
+ } else {
+ /* only add pseudo variables */
+ for(i = 0; i < b->var_count; i++) {
+ vd = &b->vardefs[b->arg_count + i];
+ if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ set_closure_from_var(ctx, cv, vd, i);
+ }
+ }
+ }
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv0 = &b->closure_var[i];
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ cv->is_local = FALSE;
+ cv->is_arg = cv0->is_arg;
+ cv->is_const = cv0->is_const;
+ cv->is_lexical = cv0->is_lexical;
+ cv->var_kind = cv0->var_kind;
+ cv->var_idx = i;
+ cv->var_name = JS_DupAtom(ctx, cv0->var_name);
+ }
+ return 0;
+}
+
+typedef struct CodeContext {
+ const uint8_t *bc_buf; /* code buffer */
+ int bc_len; /* length of the code buffer */
+ int pos; /* position past the matched code pattern */
+ int line_num; /* last visited OP_line_num parameter or -1 */
+ int op;
+ int idx;
+ int label;
+ int val;
+ JSAtom atom;
+} CodeContext;
+
+#define M2(op1, op2) ((op1) | ((op2) << 8))
+#define M3(op1, op2, op3) ((op1) | ((op2) << 8) | ((op3) << 16))
+#define M4(op1, op2, op3, op4) ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))
+
+static BOOL code_match(CodeContext *s, int pos, ...)
+{
+ const uint8_t *tab = s->bc_buf;
+ int op, len, op1, line_num, pos_next;
+ va_list ap;
+ BOOL ret = FALSE;
+
+ line_num = -1;
+ va_start(ap, pos);
+
+ for(;;) {
+ op1 = va_arg(ap, int);
+ if (op1 == -1) {
+ s->pos = pos;
+ s->line_num = line_num;
+ ret = TRUE;
+ break;
+ }
+ for (;;) {
+ if (pos >= s->bc_len)
+ goto done;
+ op = tab[pos];
+ len = opcode_info[op].size;
+ pos_next = pos + len;
+ if (pos_next > s->bc_len)
+ goto done;
+ if (op == OP_line_num) {
+ line_num = get_u32(tab + pos + 1);
+ pos = pos_next;
+ } else {
+ break;
+ }
+ }
+ if (op != op1) {
+ if (op1 == (uint8_t)op1 || !op)
+ break;
+ if (op != (uint8_t)op1
+ && op != (uint8_t)(op1 >> 8)
+ && op != (uint8_t)(op1 >> 16)
+ && op != (uint8_t)(op1 >> 24)) {
+ break;
+ }
+ s->op = op;
+ }
+
+ pos++;
+ switch(opcode_info[op].fmt) {
+ case OP_FMT_loc8:
+ case OP_FMT_u8:
+ {
+ int idx = tab[pos];
+ int arg = va_arg(ap, int);
+ if (arg == -1) {
+ s->idx = idx;
+ } else {
+ if (arg != idx)
+ goto done;
+ }
+ break;
+ }
+ case OP_FMT_u16:
+ case OP_FMT_npop:
+ case OP_FMT_loc:
+ case OP_FMT_arg:
+ case OP_FMT_var_ref:
+ {
+ int idx = get_u16(tab + pos);
+ int arg = va_arg(ap, int);
+ if (arg == -1) {
+ s->idx = idx;
+ } else {
+ if (arg != idx)
+ goto done;
+ }
+ break;
+ }
+ case OP_FMT_i32:
+ case OP_FMT_u32:
+ case OP_FMT_label:
+ case OP_FMT_const:
+ {
+ s->label = get_u32(tab + pos);
+ break;
+ }
+ case OP_FMT_label_u16:
+ {
+ s->label = get_u32(tab + pos);
+ s->val = get_u16(tab + pos + 4);
+ break;
+ }
+ case OP_FMT_atom:
+ {
+ s->atom = get_u32(tab + pos);
+ break;
+ }
+ case OP_FMT_atom_u8:
+ {
+ s->atom = get_u32(tab + pos);
+ s->val = get_u8(tab + pos + 4);
+ break;
+ }
+ case OP_FMT_atom_u16:
+ {
+ s->atom = get_u32(tab + pos);
+ s->val = get_u16(tab + pos + 4);
+ break;
+ }
+ case OP_FMT_atom_label_u8:
+ {
+ s->atom = get_u32(tab + pos);
+ s->label = get_u32(tab + pos + 4);
+ s->val = get_u8(tab + pos + 8);
+ break;
+ }
+ default:
+ break;
+ }
+ pos = pos_next;
+ }
+ done:
+ va_end(ap);
+ return ret;
+}
+
+static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)
+{
+ int i, idx, label_next = -1;
+
+ /* add the hoisted functions in arguments and local variables */
+ for(i = 0; i < s->arg_count; i++) {
+ JSVarDef *vd = &s->args[i];
+ if (vd->func_pool_idx >= 0) {
+ dbuf_putc(bc, OP_fclosure);
+ dbuf_put_u32(bc, vd->func_pool_idx);
+ dbuf_putc(bc, OP_put_arg);
+ dbuf_put_u16(bc, i);
+ }
+ }
+ for(i = 0; i < s->var_count; i++) {
+ JSVarDef *vd = &s->vars[i];
+ if (vd->scope_level == 0 && vd->func_pool_idx >= 0) {
+ dbuf_putc(bc, OP_fclosure);
+ dbuf_put_u32(bc, vd->func_pool_idx);
+ dbuf_putc(bc, OP_put_loc);
+ dbuf_put_u16(bc, i);
+ }
+ }
+
+ /* the module global variables must be initialized before
+ evaluating the module so that the exported functions are
+ visible if there are cyclic module references */
+ if (s->module) {
+ label_next = new_label_fd(s, -1);
+
+ /* if 'this' is true, initialize the global variables and return */
+ dbuf_putc(bc, OP_push_this);
+ dbuf_putc(bc, OP_if_false);
+ dbuf_put_u32(bc, label_next);
+ update_label(s, label_next, 1);
+ s->jump_size++;
+ }
+
+ /* add the global variables (only happens if s->is_global_var is
+ true) */
+ for(i = 0; i < s->global_var_count; i++) {
+ JSGlobalVar *hf = &s->global_vars[i];
+ int has_closure = 0;
+ BOOL force_init = hf->force_init;
+ /* we are in an eval, so the closure contains all the
+ enclosing variables */
+ /* If the outer function has a variable environment,
+ create a property for the variable there */
+ for(idx = 0; idx < s->closure_var_count; idx++) {
+ JSClosureVar *cv = &s->closure_var[idx];
+ if (cv->var_name == hf->var_name) {
+ has_closure = 2;
+ force_init = FALSE;
+ break;
+ }
+ if (cv->var_name == JS_ATOM__var_ ||
+ cv->var_name == JS_ATOM__arg_var_) {
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ has_closure = 1;
+ force_init = TRUE;
+ break;
+ }
+ }
+ if (!has_closure) {
+ int flags;
+
+ flags = 0;
+ if (s->eval_type != JS_EVAL_TYPE_GLOBAL)
+ flags |= JS_PROP_CONFIGURABLE;
+ if (hf->cpool_idx >= 0 && !hf->is_lexical) {
+ /* global function definitions need a specific handling */
+ dbuf_putc(bc, OP_fclosure);
+ dbuf_put_u32(bc, hf->cpool_idx);
+
+ dbuf_putc(bc, OP_define_func);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
+ dbuf_putc(bc, flags);
+
+ goto done_global_var;
+ } else {
+ if (hf->is_lexical) {
+ flags |= DEFINE_GLOBAL_LEX_VAR;
+ if (!hf->is_const)
+ flags |= JS_PROP_WRITABLE;
+ }
+ dbuf_putc(bc, OP_define_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
+ dbuf_putc(bc, flags);
+ }
+ }
+ if (hf->cpool_idx >= 0 || force_init) {
+ if (hf->cpool_idx >= 0) {
+ dbuf_putc(bc, OP_fclosure);
+ dbuf_put_u32(bc, hf->cpool_idx);
+ if (hf->var_name == JS_ATOM__default_) {
+ /* set default export function name */
+ dbuf_putc(bc, OP_set_name);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));
+ }
+ } else {
+ dbuf_putc(bc, OP_undefined);
+ }
+ if (has_closure == 2) {
+ dbuf_putc(bc, OP_put_var_ref);
+ dbuf_put_u16(bc, idx);
+ } else if (has_closure == 1) {
+ dbuf_putc(bc, OP_define_field);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
+ dbuf_putc(bc, OP_drop);
+ } else {
+ /* XXX: Check if variable is writable and enumerable */
+ dbuf_putc(bc, OP_put_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
+ }
+ }
+ done_global_var:
+ JS_FreeAtom(ctx, hf->var_name);
+ }
+
+ if (s->module) {
+ dbuf_putc(bc, OP_return_undef);
+
+ dbuf_putc(bc, OP_label);
+ dbuf_put_u32(bc, label_next);
+ s->label_slots[label_next].pos2 = bc->size;
+ }
+
+ js_free(ctx, s->global_vars);
+ s->global_vars = NULL;
+ s->global_var_count = 0;
+ s->global_var_size = 0;
+}
+
+static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,
+ int pos, int *linep)
+{
+ int op, len, label;
+
+ for (; pos < bc_len; pos += len) {
+ op = bc_buf[pos];
+ len = opcode_info[op].size;
+ if (op == OP_line_num) {
+ *linep = get_u32(bc_buf + pos + 1);
+ } else
+ if (op == OP_label) {
+ label = get_u32(bc_buf + pos + 1);
+ if (update_label(s, label, 0) > 0)
+ break;
+#if 0
+ if (s->label_slots[label].first_reloc) {
+ printf("line %d: unreferenced label %d:%d has relocations\n",
+ *linep, label, s->label_slots[label].pos2);
+ }
+#endif
+ assert(s->label_slots[label].first_reloc == NULL);
+ } else {
+ /* XXX: output a warning for unreachable code? */
+ JSAtom atom;
+ switch(opcode_info[op].fmt) {
+ case OP_FMT_label:
+ case OP_FMT_label_u16:
+ label = get_u32(bc_buf + pos + 1);
+ update_label(s, label, -1);
+ break;
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ label = get_u32(bc_buf + pos + 5);
+ update_label(s, label, -1);
+ /* fall thru */
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ case OP_FMT_atom_u16:
+ atom = get_u32(bc_buf + pos + 1);
+ JS_FreeAtom(s->ctx, atom);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return pos;
+}
+
+static int get_label_pos(JSFunctionDef *s, int label)
+{
+ int i, pos;
+ for (i = 0; i < 20; i++) {
+ pos = s->label_slots[label].pos;
+ for (;;) {
+ switch (s->byte_code.buf[pos]) {
+ case OP_line_num:
+ case OP_label:
+ pos += 5;
+ continue;
+ case OP_goto:
+ label = get_u32(s->byte_code.buf + pos + 1);
+ break;
+ default:
+ return pos;
+ }
+ break;
+ }
+ }
+ return pos;
+}
+
+/* convert global variable accesses to local variables or closure
+ variables when necessary */
+static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s)
+{
+ int pos, pos_next, bc_len, op, len, i, idx, line_num;
+ uint8_t *bc_buf;
+ JSAtom var_name;
+ DynBuf bc_out;
+ CodeContext cc;
+ int scope;
+
+ cc.bc_buf = bc_buf = s->byte_code.buf;
+ cc.bc_len = bc_len = s->byte_code.size;
+ js_dbuf_init(ctx, &bc_out);
+
+ /* first pass for runtime checks (must be done before the
+ variables are created) */
+ for(i = 0; i < s->global_var_count; i++) {
+ JSGlobalVar *hf = &s->global_vars[i];
+ int flags;
+
+ /* check if global variable (XXX: simplify) */
+ for(idx = 0; idx < s->closure_var_count; idx++) {
+ JSClosureVar *cv = &s->closure_var[idx];
+ if (cv->var_name == hf->var_name) {
+ if (s->eval_type == JS_EVAL_TYPE_DIRECT &&
+ cv->is_lexical) {
+ /* Check if a lexical variable is
+ redefined as 'var'. XXX: Could abort
+ compilation here, but for consistency
+ with the other checks, we delay the
+ error generation. */
+ dbuf_putc(&bc_out, OP_throw_error);
+ dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
+ dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);
+ }
+ goto next;
+ }
+ if (cv->var_name == JS_ATOM__var_ ||
+ cv->var_name == JS_ATOM__arg_var_)
+ goto next;
+ }
+
+ dbuf_putc(&bc_out, OP_check_define_var);
+ dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
+ flags = 0;
+ if (hf->is_lexical)
+ flags |= DEFINE_GLOBAL_LEX_VAR;
+ if (hf->cpool_idx >= 0)
+ flags |= DEFINE_GLOBAL_FUNC_VAR;
+ dbuf_putc(&bc_out, flags);
+ next: ;
+ }
+
+ line_num = 0; /* avoid warning */
+ for (pos = 0; pos < bc_len; pos = pos_next) {
+ op = bc_buf[pos];
+ len = opcode_info[op].size;
+ pos_next = pos + len;
+ switch(op) {
+ case OP_line_num:
+ line_num = get_u32(bc_buf + pos + 1);
+ s->line_number_size++;
+ goto no_change;
+
+ case OP_eval: /* convert scope index to adjusted variable index */
+ {
+ int call_argc = get_u16(bc_buf + pos + 1);
+ scope = get_u16(bc_buf + pos + 1 + 2);
+ mark_eval_captured_variables(ctx, s, scope);
+ dbuf_putc(&bc_out, op);
+ dbuf_put_u16(&bc_out, call_argc);
+ dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
+ }
+ break;
+ case OP_apply_eval: /* convert scope index to adjusted variable index */
+ scope = get_u16(bc_buf + pos + 1);
+ mark_eval_captured_variables(ctx, s, scope);
+ dbuf_putc(&bc_out, op);
+ dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
+ break;
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ case OP_scope_delete_var:
+ case OP_scope_get_ref:
+ case OP_scope_put_var_init:
+ var_name = get_u32(bc_buf + pos + 1);
+ scope = get_u16(bc_buf + pos + 5);
+ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
+ NULL, NULL, pos_next);
+ JS_FreeAtom(ctx, var_name);
+ break;
+ case OP_scope_make_ref:
+ {
+ int label;
+ LabelSlot *ls;
+ var_name = get_u32(bc_buf + pos + 1);
+ label = get_u32(bc_buf + pos + 5);
+ scope = get_u16(bc_buf + pos + 9);
+ ls = &s->label_slots[label];
+ ls->ref_count--; /* always remove label reference */
+ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
+ bc_buf, ls, pos_next);
+ JS_FreeAtom(ctx, var_name);
+ }
+ break;
+ case OP_scope_get_private_field:
+ case OP_scope_get_private_field2:
+ case OP_scope_put_private_field:
+ {
+ int ret;
+ var_name = get_u32(bc_buf + pos + 1);
+ scope = get_u16(bc_buf + pos + 5);
+ ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);
+ if (ret < 0)
+ goto fail;
+ JS_FreeAtom(ctx, var_name);
+ }
+ break;
+ case OP_gosub:
+ s->jump_size++;
+ if (OPTIMIZE) {
+ /* remove calls to empty finalizers */
+ int label;
+ LabelSlot *ls;
+
+ label = get_u32(bc_buf + pos + 1);
+ assert(label >= 0 && label < s->label_count);
+ ls = &s->label_slots[label];
+ if (code_match(&cc, ls->pos, OP_ret, -1)) {
+ ls->ref_count--;
+ break;
+ }
+ }
+ goto no_change;
+ case OP_drop:
+ if (0) {
+ /* remove drops before return_undef */
+ /* do not perform this optimization in pass2 because
+ it breaks patterns recognised in resolve_labels */
+ int pos1 = pos_next;
+ int line1 = line_num;
+ while (code_match(&cc, pos1, OP_drop, -1)) {
+ if (cc.line_num >= 0) line1 = cc.line_num;
+ pos1 = cc.pos;
+ }
+ if (code_match(&cc, pos1, OP_return_undef, -1)) {
+ pos_next = pos1;
+ if (line1 != -1 && line1 != line_num) {
+ line_num = line1;
+ s->line_number_size++;
+ dbuf_putc(&bc_out, OP_line_num);
+ dbuf_put_u32(&bc_out, line_num);
+ }
+ break;
+ }
+ }
+ goto no_change;
+ case OP_insert3:
+ if (OPTIMIZE) {
+ /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */
+ if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {
+ dbuf_putc(&bc_out, cc.op);
+ pos_next = cc.pos;
+ if (cc.line_num != -1 && cc.line_num != line_num) {
+ line_num = cc.line_num;
+ s->line_number_size++;
+ dbuf_putc(&bc_out, OP_line_num);
+ dbuf_put_u32(&bc_out, line_num);
+ }
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_goto:
+ s->jump_size++;
+ /* fall thru */
+ case OP_tail_call:
+ case OP_tail_call_method:
+ case OP_return:
+ case OP_return_undef:
+ case OP_throw:
+ case OP_throw_error:
+ case OP_ret:
+ if (OPTIMIZE) {
+ /* remove dead code */
+ int line = -1;
+ dbuf_put(&bc_out, bc_buf + pos, len);
+ pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);
+ pos_next = pos;
+ if (pos < bc_len && line >= 0 && line_num != line) {
+ line_num = line;
+ s->line_number_size++;
+ dbuf_putc(&bc_out, OP_line_num);
+ dbuf_put_u32(&bc_out, line_num);
+ }
+ break;
+ }
+ goto no_change;
+
+ case OP_label:
+ {
+ int label;
+ LabelSlot *ls;
+
+ label = get_u32(bc_buf + pos + 1);
+ assert(label >= 0 && label < s->label_count);
+ ls = &s->label_slots[label];
+ ls->pos2 = bc_out.size + opcode_info[op].size;
+ }
+ goto no_change;
+
+ case OP_enter_scope:
+ {
+ int scope_idx, scope = get_u16(bc_buf + pos + 1);
+
+ if (scope == s->body_scope) {
+ instantiate_hoisted_definitions(ctx, s, &bc_out);
+ }
+
+ for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
+ JSVarDef *vd = &s->vars[scope_idx];
+ if (vd->scope_level == scope) {
+ if (scope_idx != s->arguments_arg_idx) {
+ if (vd->var_kind == JS_VAR_FUNCTION_DECL ||
+ vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {
+ /* Initialize lexical variable upon entering scope */
+ dbuf_putc(&bc_out, OP_fclosure);
+ dbuf_put_u32(&bc_out, vd->func_pool_idx);
+ dbuf_putc(&bc_out, OP_put_loc);
+ dbuf_put_u16(&bc_out, scope_idx);
+ } else {
+ /* XXX: should check if variable can be used
+ before initialization */
+ dbuf_putc(&bc_out, OP_set_loc_uninitialized);
+ dbuf_put_u16(&bc_out, scope_idx);
+ }
+ }
+ scope_idx = vd->scope_next;
+ } else {
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_leave_scope:
+ {
+ int scope_idx, scope = get_u16(bc_buf + pos + 1);
+
+ for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
+ JSVarDef *vd = &s->vars[scope_idx];
+ if (vd->scope_level == scope) {
+ if (vd->is_captured) {
+ dbuf_putc(&bc_out, OP_close_loc);
+ dbuf_put_u16(&bc_out, scope_idx);
+ }
+ scope_idx = vd->scope_next;
+ } else {
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_set_name:
+ {
+ /* remove dummy set_name opcodes */
+ JSAtom name = get_u32(bc_buf + pos + 1);
+ if (name == JS_ATOM_NULL)
+ break;
+ }
+ goto no_change;
+
+ case OP_if_false:
+ case OP_if_true:
+ case OP_catch:
+ s->jump_size++;
+ goto no_change;
+
+ case OP_dup:
+ if (OPTIMIZE) {
+ /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */
+ /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */
+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {
+ int lab0, lab1, op1, pos1, line1, pos2;
+ lab0 = lab1 = cc.label;
+ assert(lab1 >= 0 && lab1 < s->label_count);
+ op1 = cc.op;
+ pos1 = cc.pos;
+ line1 = cc.line_num;
+ while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {
+ lab1 = cc.label;
+ }
+ if (code_match(&cc, pos2, op1, -1)) {
+ s->jump_size++;
+ update_label(s, lab0, -1);
+ update_label(s, cc.label, +1);
+ dbuf_putc(&bc_out, op1);
+ dbuf_put_u32(&bc_out, cc.label);
+ pos_next = pos1;
+ if (line1 != -1 && line1 != line_num) {
+ line_num = line1;
+ s->line_number_size++;
+ dbuf_putc(&bc_out, OP_line_num);
+ dbuf_put_u32(&bc_out, line_num);
+ }
+ break;
+ }
+ }
+ }
+ goto no_change;
+
+ case OP_nop:
+ /* remove erased code */
+ break;
+ case OP_set_class_name:
+ /* only used during parsing */
+ break;
+
+ default:
+ no_change:
+ dbuf_put(&bc_out, bc_buf + pos, len);
+ break;
+ }
+ }
+
+ /* set the new byte code */
+ dbuf_free(&s->byte_code);
+ s->byte_code = bc_out;
+ if (dbuf_error(&s->byte_code)) {
+ JS_ThrowOutOfMemory(ctx);
+ return -1;
+ }
+ return 0;
+ fail:
+ /* continue the copy to keep the atom refcounts consistent */
+ /* XXX: find a better solution ? */
+ for (; pos < bc_len; pos = pos_next) {
+ op = bc_buf[pos];
+ len = opcode_info[op].size;
+ pos_next = pos + len;
+ dbuf_put(&bc_out, bc_buf + pos, len);
+ }
+ dbuf_free(&s->byte_code);
+ s->byte_code = bc_out;
+ return -1;
+}
+
+/* the pc2line table gives a line number for each PC value */
+static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)
+{
+ if (s->line_number_slots != NULL
+ && s->line_number_count < s->line_number_size
+ && pc >= s->line_number_last_pc
+ && line_num != s->line_number_last) {
+ s->line_number_slots[s->line_number_count].pc = pc;
+ s->line_number_slots[s->line_number_count].line_num = line_num;
+ s->line_number_count++;
+ s->line_number_last_pc = pc;
+ s->line_number_last = line_num;
+ }
+}
+
+static void compute_pc2line_info(JSFunctionDef *s)
+{
+ if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {
+ int last_line_num = s->line_num;
+ uint32_t last_pc = 0;
+ int i;
+
+ js_dbuf_init(s->ctx, &s->pc2line);
+ for (i = 0; i < s->line_number_count; i++) {
+ uint32_t pc = s->line_number_slots[i].pc;
+ int line_num = s->line_number_slots[i].line_num;
+ int diff_pc, diff_line;
+
+ if (line_num < 0)
+ continue;
+
+ diff_pc = pc - last_pc;
+ diff_line = line_num - last_line_num;
+ if (diff_line == 0 || diff_pc < 0)
+ continue;
+
+ if (diff_line >= PC2LINE_BASE &&
+ diff_line < PC2LINE_BASE + PC2LINE_RANGE &&
+ diff_pc <= PC2LINE_DIFF_PC_MAX) {
+ dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) +
+ diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);
+ } else {
+ /* longer encoding */
+ dbuf_putc(&s->pc2line, 0);
+ dbuf_put_leb128(&s->pc2line, diff_pc);
+ dbuf_put_sleb128(&s->pc2line, diff_line);
+ }
+ last_pc = pc;
+ last_line_num = line_num;
+ }
+ }
+}
+
+static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)
+{
+ RelocEntry *re;
+ re = js_malloc(ctx, sizeof(*re));
+ if (!re)
+ return NULL;
+ re->addr = addr;
+ re->size = size;
+ re->next = ls->first_reloc;
+ ls->first_reloc = re;
+ return re;
+}
+
+static BOOL code_has_label(CodeContext *s, int pos, int label)
+{
+ while (pos < s->bc_len) {
+ int op = s->bc_buf[pos];
+ if (op == OP_line_num) {
+ pos += 5;
+ continue;
+ }
+ if (op == OP_label) {
+ int lab = get_u32(s->bc_buf + pos + 1);
+ if (lab == label)
+ return TRUE;
+ pos += 5;
+ continue;
+ }
+ if (op == OP_goto) {
+ int lab = get_u32(s->bc_buf + pos + 1);
+ if (lab == label)
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/* return the target label, following the OP_goto jumps
+ the first opcode at destination is stored in *pop
+ */
+static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline)
+{
+ int i, pos, op;
+
+ update_label(s, label, -1);
+ for (i = 0; i < 10; i++) {
+ assert(label >= 0 && label < s->label_count);
+ pos = s->label_slots[label].pos2;
+ for (;;) {
+ switch(op = s->byte_code.buf[pos]) {
+ case OP_line_num:
+ if (pline)
+ *pline = get_u32(s->byte_code.buf + pos + 1);
+ /* fall thru */
+ case OP_label:
+ pos += opcode_info[op].size;
+ continue;
+ case OP_goto:
+ label = get_u32(s->byte_code.buf + pos + 1);
+ break;
+ case OP_drop:
+ /* ignore drop opcodes if followed by OP_return_undef */
+ while (s->byte_code.buf[++pos] == OP_drop)
+ continue;
+ if (s->byte_code.buf[pos] == OP_return_undef)
+ op = OP_return_undef;
+ /* fall thru */
+ default:
+ goto done;
+ }
+ break;
+ }
+ }
+ /* cycle detected, could issue a warning */
+ done:
+ *pop = op;
+ update_label(s, label, +1);
+ return label;
+}
+
+static void push_short_int(DynBuf *bc_out, int val)
+{
+#if SHORT_OPCODES
+ if (val >= -1 && val <= 7) {
+ dbuf_putc(bc_out, OP_push_0 + val);
+ return;
+ }
+ if (val == (int8_t)val) {
+ dbuf_putc(bc_out, OP_push_i8);
+ dbuf_putc(bc_out, val);
+ return;
+ }
+ if (val == (int16_t)val) {
+ dbuf_putc(bc_out, OP_push_i16);
+ dbuf_put_u16(bc_out, val);
+ return;
+ }
+#endif
+ dbuf_putc(bc_out, OP_push_i32);
+ dbuf_put_u32(bc_out, val);
+}
+
+static void put_short_code(DynBuf *bc_out, int op, int idx)
+{
+#if SHORT_OPCODES
+ if (idx < 4) {
+ switch (op) {
+ case OP_get_loc:
+ dbuf_putc(bc_out, OP_get_loc0 + idx);
+ return;
+ case OP_put_loc:
+ dbuf_putc(bc_out, OP_put_loc0 + idx);
+ return;
+ case OP_set_loc:
+ dbuf_putc(bc_out, OP_set_loc0 + idx);
+ return;
+ case OP_get_arg:
+ dbuf_putc(bc_out, OP_get_arg0 + idx);
+ return;
+ case OP_put_arg:
+ dbuf_putc(bc_out, OP_put_arg0 + idx);
+ return;
+ case OP_set_arg:
+ dbuf_putc(bc_out, OP_set_arg0 + idx);
+ return;
+ case OP_get_var_ref:
+ dbuf_putc(bc_out, OP_get_var_ref0 + idx);
+ return;
+ case OP_put_var_ref:
+ dbuf_putc(bc_out, OP_put_var_ref0 + idx);
+ return;
+ case OP_set_var_ref:
+ dbuf_putc(bc_out, OP_set_var_ref0 + idx);
+ return;
+ case OP_call:
+ dbuf_putc(bc_out, OP_call0 + idx);
+ return;
+ }
+ }
+ if (idx < 256) {
+ switch (op) {
+ case OP_get_loc:
+ dbuf_putc(bc_out, OP_get_loc8);
+ dbuf_putc(bc_out, idx);
+ return;
+ case OP_put_loc:
+ dbuf_putc(bc_out, OP_put_loc8);
+ dbuf_putc(bc_out, idx);
+ return;
+ case OP_set_loc:
+ dbuf_putc(bc_out, OP_set_loc8);
+ dbuf_putc(bc_out, idx);
+ return;
+ }
+ }
+#endif
+ dbuf_putc(bc_out, op);
+ dbuf_put_u16(bc_out, idx);
+}
+
+/* peephole optimizations and resolve goto/labels */
+static warn_unused int resolve_labels(JSContext *ctx, JSFunctionDef *s)
+{
+ int pos, pos_next, bc_len, op, op1, len, i, line_num;
+ const uint8_t *bc_buf;
+ DynBuf bc_out;
+ LabelSlot *label_slots, *ls;
+ RelocEntry *re, *re_next;
+ CodeContext cc;
+ int label;
+#if SHORT_OPCODES
+ JumpSlot *jp;
+#endif
+
+ label_slots = s->label_slots;
+
+ line_num = s->line_num;
+
+ cc.bc_buf = bc_buf = s->byte_code.buf;
+ cc.bc_len = bc_len = s->byte_code.size;
+ js_dbuf_init(ctx, &bc_out);
+
+#if SHORT_OPCODES
+ if (s->jump_size) {
+ s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);
+ if (s->jump_slots == NULL)
+ return -1;
+ }
+#endif
+ /* XXX: Should skip this phase if not generating SHORT_OPCODES */
+ if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {
+ s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);
+ if (s->line_number_slots == NULL)
+ return -1;
+ s->line_number_last = s->line_num;
+ s->line_number_last_pc = 0;
+ }
+
+ /* initialize the 'home_object' variable if needed */
+ if (s->home_object_var_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);
+ put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);
+ }
+ /* initialize the 'this.active_func' variable if needed */
+ if (s->this_active_func_var_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
+ put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);
+ }
+ /* initialize the 'new.target' variable if needed */
+ if (s->new_target_var_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);
+ put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);
+ }
+ /* initialize the 'this' variable if needed. In a derived class
+ constructor, this is initially uninitialized. */
+ if (s->this_var_idx >= 0) {
+ if (s->is_derived_class_constructor) {
+ dbuf_putc(&bc_out, OP_set_loc_uninitialized);
+ dbuf_put_u16(&bc_out, s->this_var_idx);
+ } else {
+ dbuf_putc(&bc_out, OP_push_this);
+ put_short_code(&bc_out, OP_put_loc, s->this_var_idx);
+ }
+ }
+ /* initialize the 'arguments' variable if needed */
+ if (s->arguments_var_idx >= 0) {
+ if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);
+ } else {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
+ }
+ if (s->arguments_arg_idx >= 0)
+ put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
+ put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);
+ }
+ /* initialize a reference to the current function if needed */
+ if (s->func_var_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
+ put_short_code(&bc_out, OP_put_loc, s->func_var_idx);
+ }
+ /* initialize the variable environment object if needed */
+ if (s->var_object_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
+ put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
+ }
+ if (s->arg_var_object_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
+ put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);
+ }
+
+ for (pos = 0; pos < bc_len; pos = pos_next) {
+ int val;
+ op = bc_buf[pos];
+ len = opcode_info[op].size;
+ pos_next = pos + len;
+ switch(op) {
+ case OP_line_num:
+ /* line number info (for debug). We put it in a separate
+ compressed table to reduce memory usage and get better
+ performance */
+ line_num = get_u32(bc_buf + pos + 1);
+ break;
+
+ case OP_label:
+ {
+ label = get_u32(bc_buf + pos + 1);
+ assert(label >= 0 && label < s->label_count);
+ ls = &label_slots[label];
+ assert(ls->addr == -1);
+ ls->addr = bc_out.size;
+ /* resolve the relocation entries */
+ for(re = ls->first_reloc; re != NULL; re = re_next) {
+ int diff = ls->addr - re->addr;
+ re_next = re->next;
+ switch (re->size) {
+ case 4:
+ put_u32(bc_out.buf + re->addr, diff);
+ break;
+ case 2:
+ assert(diff == (int16_t)diff);
+ put_u16(bc_out.buf + re->addr, diff);
+ break;
+ case 1:
+ assert(diff == (int8_t)diff);
+ put_u8(bc_out.buf + re->addr, diff);
+ break;
+ }
+ js_free(ctx, re);
+ }
+ ls->first_reloc = NULL;
+ }
+ break;
+
+ case OP_call:
+ case OP_call_method:
+ {
+ /* detect and transform tail calls */
+ int argc;
+ argc = get_u16(bc_buf + pos + 1);
+ if (code_match(&cc, pos_next, OP_return, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op + 1, argc);
+ pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);
+ break;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op, argc);
+ break;
+ }
+ goto no_change;
+
+ case OP_return:
+ case OP_return_undef:
+ case OP_return_async:
+ case OP_throw:
+ case OP_throw_error:
+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
+ goto no_change;
+
+ case OP_goto:
+ label = get_u32(bc_buf + pos + 1);
+ has_goto:
+ if (OPTIMIZE) {
+ int line1 = -1;
+ /* Use custom matcher because multiple labels can follow */
+ label = find_jump_target(s, label, &op1, &line1);
+ if (code_has_label(&cc, pos_next, label)) {
+ /* jump to next instruction: remove jump */
+ update_label(s, label, -1);
+ break;
+ }
+ if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {
+ /* jump to return/throw: remove jump, append return/throw */
+ /* updating the line number obfuscates assembly listing */
+ //if (line1 >= 0) line_num = line1;
+ update_label(s, label, -1);
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, op1);
+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
+ break;
+ }
+ /* XXX: should duplicate single instructions followed by goto or return */
+ /* For example, can match one of these followed by return:
+ push_i32 / push_const / push_atom_value / get_var /
+ undefined / null / push_false / push_true / get_ref_value /
+ get_loc / get_arg / get_var_ref
+ */
+ }
+ goto has_label;
+
+ case OP_gosub:
+ label = get_u32(bc_buf + pos + 1);
+ if (0 && OPTIMIZE) {
+ label = find_jump_target(s, label, &op1, NULL);
+ if (op1 == OP_ret) {
+ update_label(s, label, -1);
+ /* empty finally clause: remove gosub */
+ break;
+ }
+ }
+ goto has_label;
+
+ case OP_catch:
+ label = get_u32(bc_buf + pos + 1);
+ goto has_label;
+
+ case OP_if_true:
+ case OP_if_false:
+ label = get_u32(bc_buf + pos + 1);
+ if (OPTIMIZE) {
+ label = find_jump_target(s, label, &op1, NULL);
+ /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */
+ if (code_has_label(&cc, pos_next, label)) {
+ update_label(s, label, -1);
+ dbuf_putc(&bc_out, OP_drop);
+ break;
+ }
+ /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */
+ if (code_match(&cc, pos_next, OP_goto, -1)) {
+ int pos1 = cc.pos;
+ int line1 = cc.line_num;
+ if (code_has_label(&cc, pos1, label)) {
+ if (line1 >= 0) line_num = line1;
+ pos_next = pos1;
+ update_label(s, label, -1);
+ label = cc.label;
+ op ^= OP_if_true ^ OP_if_false;
+ }
+ }
+ }
+ has_label:
+ add_pc2line_info(s, bc_out.size, line_num);
+ if (op == OP_goto) {
+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
+ }
+ assert(label >= 0 && label < s->label_count);
+ ls = &label_slots[label];
+#if SHORT_OPCODES
+ jp = &s->jump_slots[s->jump_count++];
+ jp->op = op;
+ jp->size = 4;
+ jp->pos = bc_out.size + 1;
+ jp->label = label;
+
+ if (ls->addr == -1) {
+ int diff = ls->pos2 - pos - 1;
+ if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
+ jp->size = 1;
+ jp->op = OP_if_false8 + (op - OP_if_false);
+ dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
+ dbuf_putc(&bc_out, 0);
+ if (!add_reloc(ctx, ls, bc_out.size - 1, 1))
+ goto fail;
+ break;
+ }
+ if (diff < 32768 && op == OP_goto) {
+ jp->size = 2;
+ jp->op = OP_goto16;
+ dbuf_putc(&bc_out, OP_goto16);
+ dbuf_put_u16(&bc_out, 0);
+ if (!add_reloc(ctx, ls, bc_out.size - 2, 2))
+ goto fail;
+ break;
+ }
+ } else {
+ int diff = ls->addr - bc_out.size - 1;
+ if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
+ jp->size = 1;
+ jp->op = OP_if_false8 + (op - OP_if_false);
+ dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
+ dbuf_putc(&bc_out, diff);
+ break;
+ }
+ if (diff == (int16_t)diff && op == OP_goto) {
+ jp->size = 2;
+ jp->op = OP_goto16;
+ dbuf_putc(&bc_out, OP_goto16);
+ dbuf_put_u16(&bc_out, diff);
+ break;
+ }
+ }
+#endif
+ dbuf_putc(&bc_out, op);
+ dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
+ if (ls->addr == -1) {
+ /* unresolved yet: create a new relocation entry */
+ if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
+ goto fail;
+ }
+ break;
+ case OP_with_get_var:
+ case OP_with_put_var:
+ case OP_with_delete_var:
+ case OP_with_make_ref:
+ case OP_with_get_ref:
+ case OP_with_get_ref_undef:
+ {
+ JSAtom atom;
+ int is_with;
+
+ atom = get_u32(bc_buf + pos + 1);
+ label = get_u32(bc_buf + pos + 5);
+ is_with = bc_buf[pos + 9];
+ if (OPTIMIZE) {
+ label = find_jump_target(s, label, &op1, NULL);
+ }
+ assert(label >= 0 && label < s->label_count);
+ ls = &label_slots[label];
+ add_pc2line_info(s, bc_out.size, line_num);
+#if SHORT_OPCODES
+ jp = &s->jump_slots[s->jump_count++];
+ jp->op = op;
+ jp->size = 4;
+ jp->pos = bc_out.size + 5;
+ jp->label = label;
+#endif
+ dbuf_putc(&bc_out, op);
+ dbuf_put_u32(&bc_out, atom);
+ dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
+ if (ls->addr == -1) {
+ /* unresolved yet: create a new relocation entry */
+ if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
+ goto fail;
+ }
+ dbuf_putc(&bc_out, is_with);
+ }
+ break;
+
+ case OP_drop:
+ if (OPTIMIZE) {
+ /* remove useless drops before return */
+ if (code_match(&cc, pos_next, OP_return_undef, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_null:
+#if SHORT_OPCODES
+ if (OPTIMIZE) {
+ /* transform null strict_eq into is_null */
+ if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_is_null);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */
+ if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_is_null);
+ pos_next = cc.pos;
+ label = cc.label;
+ op = cc.op ^ OP_if_false ^ OP_if_true;
+ goto has_label;
+ }
+ }
+#endif
+ /* fall thru */
+ case OP_push_false:
+ case OP_push_true:
+ if (OPTIMIZE) {
+ val = (op == OP_push_true);
+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
+ has_constant_test:
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ if (val == cc.op - OP_if_false) {
+ /* transform null if_false(l1) -> goto l1 */
+ /* transform false if_false(l1) -> goto l1 */
+ /* transform true if_true(l1) -> goto l1 */
+ pos_next = cc.pos;
+ op = OP_goto;
+ label = cc.label;
+ goto has_goto;
+ } else {
+ /* transform null if_true(l1) -> nop */
+ /* transform false if_true(l1) -> nop */
+ /* transform true if_false(l1) -> nop */
+ pos_next = cc.pos;
+ update_label(s, cc.label, -1);
+ break;
+ }
+ }
+ }
+ goto no_change;
+
+ case OP_push_i32:
+ if (OPTIMIZE) {
+ /* transform i32(val) neg -> i32(-val) */
+ val = get_i32(bc_buf + pos + 1);
+ if ((val != INT32_MIN && val != 0)
+ && code_match(&cc, pos_next, OP_neg, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ if (code_match(&cc, cc.pos, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ } else {
+ add_pc2line_info(s, bc_out.size, line_num);
+ push_short_int(&bc_out, -val);
+ }
+ pos_next = cc.pos;
+ break;
+ }
+ /* remove push/drop pairs generated by the parser */
+ if (code_match(&cc, pos_next, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ pos_next = cc.pos;
+ break;
+ }
+ /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */
+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
+ val = (val != 0);
+ goto has_constant_test;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ push_short_int(&bc_out, val);
+ break;
+ }
+ goto no_change;
+
+#if SHORT_OPCODES
+ case OP_push_const:
+ case OP_fclosure:
+ if (OPTIMIZE) {
+ int idx = get_u32(bc_buf + pos + 1);
+ if (idx < 256) {
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);
+ dbuf_putc(&bc_out, idx);
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_get_field:
+ if (OPTIMIZE) {
+ JSAtom atom = get_u32(bc_buf + pos + 1);
+ if (atom == JS_ATOM_length) {
+ JS_FreeAtom(ctx, atom);
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_get_length);
+ break;
+ }
+ }
+ goto no_change;
+#endif
+ case OP_push_atom_value:
+ if (OPTIMIZE) {
+ JSAtom atom = get_u32(bc_buf + pos + 1);
+ /* remove push/drop pairs generated by the parser */
+ if (code_match(&cc, pos_next, OP_drop, -1)) {
+ JS_FreeAtom(ctx, atom);
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ pos_next = cc.pos;
+ break;
+ }
+#if SHORT_OPCODES
+ if (atom == JS_ATOM_empty_string) {
+ JS_FreeAtom(ctx, atom);
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_push_empty_string);
+ break;
+ }
+#endif
+ }
+ goto no_change;
+
+ case OP_to_propkey:
+ case OP_to_propkey2:
+ if (OPTIMIZE) {
+ /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */
+ if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)
+ || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)
+ || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_undefined:
+ if (OPTIMIZE) {
+ /* remove push/drop pairs generated by the parser */
+ if (code_match(&cc, pos_next, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ pos_next = cc.pos;
+ break;
+ }
+ /* transform undefined return -> return_undefined */
+ if (code_match(&cc, pos_next, OP_return, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_return_undef);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */
+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
+ val = 0;
+ goto has_constant_test;
+ }
+#if SHORT_OPCODES
+ /* transform undefined strict_eq -> is_undefined */
+ if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_is_undefined);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */
+ if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_is_undefined);
+ pos_next = cc.pos;
+ label = cc.label;
+ op = cc.op ^ OP_if_false ^ OP_if_true;
+ goto has_label;
+ }
+#endif
+ }
+ goto no_change;
+
+ case OP_insert2:
+ if (OPTIMIZE) {
+ /* Transformation:
+ insert2 put_field(a) drop -> put_field(a)
+ insert2 put_var_strict(a) drop -> put_var_strict(a)
+ */
+ if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, cc.op);
+ dbuf_put_u32(&bc_out, cc.atom);
+ pos_next = cc.pos;
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_dup:
+ if (OPTIMIZE) {
+ /* Transformation: dup put_x(n) drop -> put_x(n) */
+ int op1, line2 = -1;
+ /* Transformation: dup put_x(n) -> set_x(n) */
+ if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ op1 = cc.op + 1; /* put_x -> set_x */
+ pos_next = cc.pos;
+ if (code_match(&cc, cc.pos, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ op1 -= 1; /* set_x drop -> put_x */
+ pos_next = cc.pos;
+ if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {
+ line2 = cc.line_num; /* delay line number update */
+ op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */
+ pos_next = cc.pos;
+ }
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op1, cc.idx);
+ if (line2 >= 0) line_num = line2;
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_get_loc:
+ if (OPTIMIZE) {
+ /* transformation:
+ get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)
+ get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)
+ get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)
+ get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)
+ */
+ int idx;
+ idx = get_u16(bc_buf + pos + 1);
+ if (idx >= 256)
+ goto no_change;
+ if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||
+ code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);
+ dbuf_putc(&bc_out, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transformation:
+ get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)
+ */
+ if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+#if SHORT_OPCODES
+ if (cc.atom == JS_ATOM_empty_string) {
+ JS_FreeAtom(ctx, cc.atom);
+ dbuf_putc(&bc_out, OP_push_empty_string);
+ } else
+#endif
+ {
+ dbuf_putc(&bc_out, OP_push_atom_value);
+ dbuf_put_u32(&bc_out, cc.atom);
+ }
+ dbuf_putc(&bc_out, OP_add_loc);
+ dbuf_putc(&bc_out, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transformation:
+ get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)
+ */
+ if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ push_short_int(&bc_out, cc.label);
+ dbuf_putc(&bc_out, OP_add_loc);
+ dbuf_putc(&bc_out, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transformation: XXX: also do these:
+ get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)
+ get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)
+ get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)
+ */
+ if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, cc.op, cc.idx);
+ dbuf_putc(&bc_out, OP_add_loc);
+ dbuf_putc(&bc_out, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op, idx);
+ break;
+ }
+ goto no_change;
+#if SHORT_OPCODES
+ case OP_get_arg:
+ case OP_get_var_ref:
+ if (OPTIMIZE) {
+ int idx;
+ idx = get_u16(bc_buf + pos + 1);
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op, idx);
+ break;
+ }
+ goto no_change;
+#endif
+ case OP_put_loc:
+ case OP_put_arg:
+ case OP_put_var_ref:
+ if (OPTIMIZE) {
+ /* transformation: put_x(n) get_x(n) -> set_x(n) */
+ int idx;
+ idx = get_u16(bc_buf + pos + 1);
+ if (code_match(&cc, pos_next, op - 1, idx, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op + 1, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op, idx);
+ break;
+ }
+ goto no_change;
+
+ case OP_post_inc:
+ case OP_post_dec:
+ if (OPTIMIZE) {
+ /* transformation:
+ post_inc put_x drop -> inc put_x
+ post_inc perm3 put_field drop -> inc put_field
+ post_inc perm3 put_var_strict drop -> inc put_var_strict
+ post_inc perm4 put_array_el drop -> inc put_array_el
+ */
+ int op1, idx;
+ if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ op1 = cc.op;
+ idx = cc.idx;
+ pos_next = cc.pos;
+ if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */
+ pos_next = cc.pos;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
+ put_short_code(&bc_out, op1, idx);
+ break;
+ }
+ if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
+ dbuf_putc(&bc_out, cc.op);
+ dbuf_put_u32(&bc_out, cc.atom);
+ pos_next = cc.pos;
+ break;
+ }
+ if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
+ dbuf_putc(&bc_out, OP_put_array_el);
+ pos_next = cc.pos;
+ break;
+ }
+ }
+ goto no_change;
+
+#if SHORT_OPCODES
+ case OP_typeof:
+ if (OPTIMIZE) {
+ /* simplify typeof tests */
+ if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
+ int op2 = -1;
+ switch (cc.atom) {
+ case JS_ATOM_undefined:
+ op2 = OP_typeof_is_undefined;
+ break;
+ case JS_ATOM_function:
+ op2 = OP_typeof_is_function;
+ break;
+ }
+ if (op2 >= 0) {
+ /* transform typeof(s) == "<type>" into is_<type> */
+ if (op1 == OP_strict_eq) {
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, op2);
+ JS_FreeAtom(ctx, cc.atom);
+ pos_next = cc.pos;
+ break;
+ }
+ if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {
+ /* transform typeof(s) != "<type>" if_false into is_<type> if_true */
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, op2);
+ JS_FreeAtom(ctx, cc.atom);
+ pos_next = cc.pos;
+ label = cc.label;
+ op = OP_if_true;
+ goto has_label;
+ }
+ }
+ }
+ }
+ goto no_change;
+#endif
+
+ default:
+ no_change:
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_put(&bc_out, bc_buf + pos, len);
+ break;
+ }
+ }
+
+ /* check that there were no missing labels */
+ for(i = 0; i < s->label_count; i++) {
+ assert(label_slots[i].first_reloc == NULL);
+ }
+#if SHORT_OPCODES
+ if (OPTIMIZE) {
+ /* more jump optimizations */
+ int patch_offsets = 0;
+ for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {
+ LabelSlot *ls;
+ JumpSlot *jp1;
+ int j, pos, diff, delta;
+
+ delta = 3;
+ switch (op = jp->op) {
+ case OP_goto16:
+ delta = 1;
+ /* fall thru */
+ case OP_if_false:
+ case OP_if_true:
+ case OP_goto:
+ pos = jp->pos;
+ diff = s->label_slots[jp->label].addr - pos;
+ if (diff >= -128 && diff <= 127 + delta) {
+ //put_u8(bc_out.buf + pos, diff);
+ jp->size = 1;
+ if (op == OP_goto16) {
+ bc_out.buf[pos - 1] = jp->op = OP_goto8;
+ } else {
+ bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
+ }
+ goto shrink;
+ } else if (diff == (int16_t)diff && op == OP_goto) {
+ //put_u16(bc_out.buf + pos, diff);
+ jp->size = 2;
+ delta = 2;
+ bc_out.buf[pos - 1] = jp->op = OP_goto16;
+ shrink:
+ /* XXX: should reduce complexity, using 2 finger copy scheme */
+ memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,
+ bc_out.size - pos - jp->size - delta);
+ bc_out.size -= delta;
+ patch_offsets++;
+ for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {
+ if (ls->addr > pos)
+ ls->addr -= delta;
+ }
+ for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {
+ if (jp1->pos > pos)
+ jp1->pos -= delta;
+ }
+ for (j = 0; j < s->line_number_count; j++) {
+ if (s->line_number_slots[j].pc > pos)
+ s->line_number_slots[j].pc -= delta;
+ }
+ continue;
+ }
+ break;
+ }
+ }
+ if (patch_offsets) {
+ JumpSlot *jp1;
+ int j;
+ for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {
+ int diff1 = s->label_slots[jp1->label].addr - jp1->pos;
+ switch (jp1->size) {
+ case 1:
+ put_u8(bc_out.buf + jp1->pos, diff1);
+ break;
+ case 2:
+ put_u16(bc_out.buf + jp1->pos, diff1);
+ break;
+ case 4:
+ put_u32(bc_out.buf + jp1->pos, diff1);
+ break;
+ }
+ }
+ }
+ }
+ js_free(ctx, s->jump_slots);
+ s->jump_slots = NULL;
+#endif
+ js_free(ctx, s->label_slots);
+ s->label_slots = NULL;
+ /* XXX: should delay until copying to runtime bytecode function */
+ compute_pc2line_info(s);
+ js_free(ctx, s->line_number_slots);
+ s->line_number_slots = NULL;
+ /* set the new byte code */
+ dbuf_free(&s->byte_code);
+ s->byte_code = bc_out;
+ s->use_short_opcodes = TRUE;
+ if (dbuf_error(&s->byte_code)) {
+ JS_ThrowOutOfMemory(ctx);
+ return -1;
+ }
+ return 0;
+ fail:
+ /* XXX: not safe */
+ dbuf_free(&bc_out);
+ return -1;
+}
+
+/* compute the maximum stack size needed by the function */
+
+typedef struct StackSizeState {
+ int bc_len;
+ int stack_len_max;
+ uint16_t *stack_level_tab;
+ int *pc_stack;
+ int pc_stack_len;
+ int pc_stack_size;
+} StackSizeState;
+
+/* 'op' is only used for error indication */
+static warn_unused int ss_check(JSContext *ctx, StackSizeState *s,
+ int pos, int op, int stack_len)
+{
+ if ((unsigned)pos >= s->bc_len) {
+ JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
+ return -1;
+ }
+ if (stack_len > s->stack_len_max) {
+ s->stack_len_max = stack_len;
+ if (s->stack_len_max > JS_STACK_SIZE_MAX) {
+ JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
+ return -1;
+ }
+ }
+ if (s->stack_level_tab[pos] != 0xffff) {
+ /* already explored: check that the stack size is consistent */
+ if (s->stack_level_tab[pos] != stack_len) {
+ JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)",
+ s->stack_level_tab[pos], stack_len, pos);
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ /* mark as explored and store the stack size */
+ s->stack_level_tab[pos] = stack_len;
+
+ /* queue the new PC to explore */
+ if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
+ &s->pc_stack_size, s->pc_stack_len + 1))
+ return -1;
+ s->pc_stack[s->pc_stack_len++] = pos;
+ return 0;
+}
+
+static warn_unused int compute_stack_size(JSContext *ctx,
+ JSFunctionDef *fd,
+ int *pstack_size)
+{
+ StackSizeState s_s, *s = &s_s;
+ int i, diff, n_pop, pos_next, stack_len, pos, op;
+ const JSOpCode *oi;
+ const uint8_t *bc_buf;
+
+ bc_buf = fd->byte_code.buf;
+ s->bc_len = fd->byte_code.size;
+ /* bc_len > 0 */
+ s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) *
+ s->bc_len);
+ if (!s->stack_level_tab)
+ return -1;
+ for(i = 0; i < s->bc_len; i++)
+ s->stack_level_tab[i] = 0xffff;
+ s->stack_len_max = 0;
+ s->pc_stack = NULL;
+ s->pc_stack_len = 0;
+ s->pc_stack_size = 0;
+
+ /* breadth-first graph exploration */
+ if (ss_check(ctx, s, 0, OP_invalid, 0))
+ goto fail;
+
+ while (s->pc_stack_len > 0) {
+ pos = s->pc_stack[--s->pc_stack_len];
+ stack_len = s->stack_level_tab[pos];
+ op = bc_buf[pos];
+ if (op == 0 || op >= OP_COUNT) {
+ JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
+ goto fail;
+ }
+ oi = &short_opcode_info(op);
+ pos_next = pos + oi->size;
+ if (pos_next > s->bc_len) {
+ JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
+ goto fail;
+ }
+ n_pop = oi->n_pop;
+ /* call pops a variable number of arguments */
+ if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {
+ n_pop += get_u16(bc_buf + pos + 1);
+ } else {
+#if SHORT_OPCODES
+ if (oi->fmt == OP_FMT_npopx) {
+ n_pop += op - OP_call0;
+ }
+#endif
+ }
+
+ if (stack_len < n_pop) {
+ JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);
+ goto fail;
+ }
+ stack_len += oi->n_push - n_pop;
+ if (stack_len > s->stack_len_max) {
+ s->stack_len_max = stack_len;
+ if (s->stack_len_max > JS_STACK_SIZE_MAX) {
+ JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
+ goto fail;
+ }
+ }
+ switch(op) {
+ case OP_tail_call:
+ case OP_tail_call_method:
+ case OP_return:
+ case OP_return_undef:
+ case OP_return_async:
+ case OP_throw:
+ case OP_throw_error:
+ case OP_ret:
+ goto done_insn;
+ case OP_goto:
+ diff = get_u32(bc_buf + pos + 1);
+ pos_next = pos + 1 + diff;
+ break;
+#if SHORT_OPCODES
+ case OP_goto16:
+ diff = (int16_t)get_u16(bc_buf + pos + 1);
+ pos_next = pos + 1 + diff;
+ break;
+ case OP_goto8:
+ diff = (int8_t)bc_buf[pos + 1];
+ pos_next = pos + 1 + diff;
+ break;
+ case OP_if_true8:
+ case OP_if_false8:
+ diff = (int8_t)bc_buf[pos + 1];
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
+ goto fail;
+ break;
+#endif
+ case OP_if_true:
+ case OP_if_false:
+ case OP_catch:
+ diff = get_u32(bc_buf + pos + 1);
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
+ goto fail;
+ break;
+ case OP_gosub:
+ diff = get_u32(bc_buf + pos + 1);
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1))
+ goto fail;
+ break;
+ case OP_with_get_var:
+ case OP_with_delete_var:
+ diff = get_u32(bc_buf + pos + 5);
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1))
+ goto fail;
+ break;
+ case OP_with_make_ref:
+ case OP_with_get_ref:
+ case OP_with_get_ref_undef:
+ diff = get_u32(bc_buf + pos + 5);
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2))
+ goto fail;
+ break;
+ case OP_with_put_var:
+ diff = get_u32(bc_buf + pos + 5);
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1))
+ goto fail;
+ break;
+
+ default:
+ break;
+ }
+ if (ss_check(ctx, s, pos_next, op, stack_len))
+ goto fail;
+ done_insn: ;
+ }
+ js_free(ctx, s->stack_level_tab);
+ js_free(ctx, s->pc_stack);
+ *pstack_size = s->stack_len_max;
+ return 0;
+ fail:
+ js_free(ctx, s->stack_level_tab);
+ js_free(ctx, s->pc_stack);
+ *pstack_size = 0;
+ return -1;
+}
+
+static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)
+{
+ int i, idx;
+ JSModuleDef *m = fd->module;
+ JSExportEntry *me;
+ JSGlobalVar *hf;
+
+ /* The imported global variables were added as closure variables
+ in js_parse_import(). We add here the module global
+ variables. */
+
+ for(i = 0; i < fd->global_var_count; i++) {
+ hf = &fd->global_vars[i];
+ if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,
+ hf->is_lexical, FALSE) < 0)
+ return -1;
+ }
+
+ /* resolve the variable names of the local exports */
+ for(i = 0; i < m->export_entries_count; i++) {
+ me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ idx = find_closure_var(ctx, fd, me->local_name);
+ if (idx < 0) {
+ JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",
+ me->local_name);
+ return -1;
+ }
+ me->u.local.var_idx = idx;
+ }
+ }
+ return 0;
+}
+
+/* create a function object from a function definition. The function
+ definition is freed. All the child functions are also created. It
+ must be done this way to resolve all the variables. */
+static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
+{
+ JSValue func_obj;
+ JSFunctionBytecode *b;
+ struct list_head *el, *el1;
+ int stack_size, scope, idx;
+ int function_size, byte_code_offset, cpool_offset;
+ int closure_var_offset, vardefs_offset;
+
+ /* recompute scope linkage */
+ for (scope = 0; scope < fd->scope_count; scope++) {
+ fd->scopes[scope].first = -1;
+ }
+ if (fd->has_parameter_expressions) {
+ /* special end of variable list marker for the argument scope */
+ fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END;
+ }
+ for (idx = 0; idx < fd->var_count; idx++) {
+ JSVarDef *vd = &fd->vars[idx];
+ vd->scope_next = fd->scopes[vd->scope_level].first;
+ fd->scopes[vd->scope_level].first = idx;
+ }
+ for (scope = 2; scope < fd->scope_count; scope++) {
+ JSVarScope *sd = &fd->scopes[scope];
+ if (sd->first < 0)
+ sd->first = fd->scopes[sd->parent].first;
+ }
+ for (idx = 0; idx < fd->var_count; idx++) {
+ JSVarDef *vd = &fd->vars[idx];
+ if (vd->scope_next < 0 && vd->scope_level > 1) {
+ scope = fd->scopes[vd->scope_level].parent;
+ vd->scope_next = fd->scopes[scope].first;
+ }
+ }
+
+ /* if the function contains an eval call, the closure variables
+ are used to compile the eval and they must be ordered by scope,
+ so it is necessary to create the closure variables before any
+ other variable lookup is done. */
+ if (fd->has_eval_call)
+ add_eval_variables(ctx, fd);
+
+ /* add the module global variables in the closure */
+ if (fd->module) {
+ if (add_module_variables(ctx, fd))
+ goto fail;
+ }
+
+ /* first create all the child functions */
+ list_for_each_safe(el, el1, &fd->child_list) {
+ JSFunctionDef *fd1;
+ int cpool_idx;
+
+ fd1 = list_entry(el, JSFunctionDef, link);
+ cpool_idx = fd1->parent_cpool_idx;
+ func_obj = js_create_function(ctx, fd1);
+ if (JS_IsException(func_obj))
+ goto fail;
+ /* save it in the constant pool */
+ assert(cpool_idx >= 0);
+ fd->cpool[cpool_idx] = func_obj;
+ }
+
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ printf("pass 1\n");
+ dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
+ fd->args, fd->arg_count, fd->vars, fd->var_count,
+ fd->closure_var, fd->closure_var_count,
+ fd->cpool, fd->cpool_count, fd->source, fd->line_num,
+ fd->label_slots, NULL);
+ printf("\n");
+ }
+#endif
+
+ if (resolve_variables(ctx, fd))
+ goto fail;
+
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ printf("pass 2\n");
+ dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
+ fd->args, fd->arg_count, fd->vars, fd->var_count,
+ fd->closure_var, fd->closure_var_count,
+ fd->cpool, fd->cpool_count, fd->source, fd->line_num,
+ fd->label_slots, NULL);
+ printf("\n");
+ }
+#endif
+
+ if (resolve_labels(ctx, fd))
+ goto fail;
+
+ if (compute_stack_size(ctx, fd, &stack_size) < 0)
+ goto fail;
+
+ if (fd->js_mode & JS_MODE_STRIP) {
+ function_size = offsetof(JSFunctionBytecode, debug);
+ } else {
+ function_size = sizeof(*b);
+ }
+ cpool_offset = function_size;
+ function_size += fd->cpool_count * sizeof(*fd->cpool);
+ vardefs_offset = function_size;
+ if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {
+ function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);
+ }
+ closure_var_offset = function_size;
+ function_size += fd->closure_var_count * sizeof(*fd->closure_var);
+ byte_code_offset = function_size;
+ function_size += fd->byte_code.size;
+
+ b = js_mallocz(ctx, function_size);
+ if (!b)
+ goto fail;
+ b->header.ref_count = 1;
+
+ b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);
+ b->byte_code_len = fd->byte_code.size;
+ memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);
+ js_free(ctx, fd->byte_code.buf);
+ fd->byte_code.buf = NULL;
+
+ b->func_name = fd->func_name;
+ if (fd->arg_count + fd->var_count > 0) {
+ if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {
+ /* Strip variable definitions not needed at runtime */
+ int i;
+ for(i = 0; i < fd->var_count; i++) {
+ JS_FreeAtom(ctx, fd->vars[i].var_name);
+ }
+ for(i = 0; i < fd->arg_count; i++) {
+ JS_FreeAtom(ctx, fd->args[i].var_name);
+ }
+ for(i = 0; i < fd->closure_var_count; i++) {
+ JS_FreeAtom(ctx, fd->closure_var[i].var_name);
+ fd->closure_var[i].var_name = JS_ATOM_NULL;
+ }
+ } else {
+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
+ if (fd->arg_count)
+ memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
+ if (fd->var_count)
+ memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
+ }
+ b->var_count = fd->var_count;
+ b->arg_count = fd->arg_count;
+ b->defined_arg_count = fd->defined_arg_count;
+ js_free(ctx, fd->args);
+ js_free(ctx, fd->vars);
+ }
+ b->cpool_count = fd->cpool_count;
+ if (b->cpool_count) {
+ b->cpool = (void *)((uint8_t*)b + cpool_offset);
+ memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));
+ }
+ js_free(ctx, fd->cpool);
+ fd->cpool = NULL;
+
+ b->stack_size = stack_size;
+
+ if (fd->js_mode & JS_MODE_STRIP) {
+ JS_FreeAtom(ctx, fd->filename);
+ dbuf_free(&fd->pc2line); // probably useless
+ } else {
+ /* XXX: source and pc2line info should be packed at the end of the
+ JSFunctionBytecode structure, avoiding allocation overhead
+ */
+ b->has_debug = 1;
+ b->debug.filename = fd->filename;
+ b->debug.line_num = fd->line_num;
+
+ //DynBuf pc2line;
+ //compute_pc2line_info(fd, &pc2line);
+ //js_free(ctx, fd->line_number_slots)
+ b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);
+ if (!b->debug.pc2line_buf)
+ b->debug.pc2line_buf = fd->pc2line.buf;
+ b->debug.pc2line_len = fd->pc2line.size;
+ b->debug.source = fd->source;
+ b->debug.source_len = fd->source_len;
+ }
+ if (fd->scopes != fd->def_scope_array)
+ js_free(ctx, fd->scopes);
+
+ b->closure_var_count = fd->closure_var_count;
+ if (b->closure_var_count) {
+ b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
+ memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
+ }
+ js_free(ctx, fd->closure_var);
+ fd->closure_var = NULL;
+
+ b->has_prototype = fd->has_prototype;
+ b->has_simple_parameter_list = fd->has_simple_parameter_list;
+ b->js_mode = fd->js_mode;
+ b->is_derived_class_constructor = fd->is_derived_class_constructor;
+ b->func_kind = fd->func_kind;
+ b->need_home_object = (fd->home_object_var_idx >= 0 ||
+ fd->need_home_object);
+ b->new_target_allowed = fd->new_target_allowed;
+ b->super_call_allowed = fd->super_call_allowed;
+ b->super_allowed = fd->super_allowed;
+ b->arguments_allowed = fd->arguments_allowed;
+ b->backtrace_barrier = fd->backtrace_barrier;
+ b->realm = JS_DupContext(ctx);
+
+ add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
+
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ js_dump_function_bytecode(ctx, b);
+ }
+#endif
+
+ if (fd->parent) {
+ /* remove from parent list */
+ list_del(&fd->link);
+ }
+
+ js_free(ctx, fd);
+ return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
+ fail:
+ js_free_function_def(ctx, fd);
+ return JS_EXCEPTION;
+}
+
+static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
+{
+ int i;
+
+#if 0
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ printf("freeing %s\n",
+ JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
+ }
+#endif
+ free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);
+
+ if (b->vardefs) {
+ for(i = 0; i < b->arg_count + b->var_count; i++) {
+ JS_FreeAtomRT(rt, b->vardefs[i].var_name);
+ }
+ }
+ for(i = 0; i < b->cpool_count; i++)
+ JS_FreeValueRT(rt, b->cpool[i]);
+
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ JS_FreeAtomRT(rt, cv->var_name);
+ }
+ if (b->realm)
+ JS_FreeContext(b->realm);
+
+ JS_FreeAtomRT(rt, b->func_name);
+ if (b->has_debug) {
+ JS_FreeAtomRT(rt, b->debug.filename);
+ js_free_rt(rt, b->debug.pc2line_buf);
+ js_free_rt(rt, b->debug.source);
+ }
+
+ remove_gc_object(&b->header);
+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
+ list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
+ } else {
+ js_free_rt(rt, b);
+ }
+}
+
+static warn_unused int js_parse_directives(JSParseState *s)
+{
+ char str[20];
+ JSParsePos pos;
+ BOOL has_semi;
+
+ if (s->token.val != TOK_STRING)
+ return 0;
+
+ js_parse_get_pos(s, &pos);
+
+ while(s->token.val == TOK_STRING) {
+ /* Copy actual source string representation */
+ snprintf(str, sizeof str, "%.*s",
+ (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);
+
+ if (next_token(s))
+ return -1;
+
+ has_semi = FALSE;
+ switch (s->token.val) {
+ case ';':
+ if (next_token(s))
+ return -1;
+ has_semi = TRUE;
+ break;
+ case '}':
+ case TOK_EOF:
+ has_semi = TRUE;
+ break;
+ case TOK_NUMBER:
+ case TOK_STRING:
+ case TOK_TEMPLATE:
+ case TOK_IDENT:
+ case TOK_REGEXP:
+ case TOK_DEC:
+ case TOK_INC:
+ case TOK_NULL:
+ case TOK_FALSE:
+ case TOK_TRUE:
+ case TOK_IF:
+ case TOK_RETURN:
+ case TOK_VAR:
+ case TOK_THIS:
+ case TOK_DELETE:
+ case TOK_TYPEOF:
+ case TOK_NEW:
+ case TOK_DO:
+ case TOK_WHILE:
+ case TOK_FOR:
+ case TOK_SWITCH:
+ case TOK_THROW:
+ case TOK_TRY:
+ case TOK_FUNCTION:
+ case TOK_DEBUGGER:
+ case TOK_WITH:
+ case TOK_CLASS:
+ case TOK_CONST:
+ case TOK_ENUM:
+ case TOK_EXPORT:
+ case TOK_IMPORT:
+ case TOK_SUPER:
+ case TOK_INTERFACE:
+ case TOK_LET:
+ case TOK_PACKAGE:
+ case TOK_PRIVATE:
+ case TOK_PROTECTED:
+ case TOK_PUBLIC:
+ case TOK_STATIC:
+ /* automatic insertion of ';' */
+ if (s->got_lf)
+ has_semi = TRUE;
+ break;
+ default:
+ break;
+ }
+ if (!has_semi)
+ break;
+ if (!strcmp(str, "use strict")) {
+ s->cur_func->has_use_strict = TRUE;
+ s->cur_func->js_mode |= JS_MODE_STRICT;
+ }
+#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)
+ else if (!strcmp(str, "use strip")) {
+ s->cur_func->js_mode |= JS_MODE_STRIP;
+ }
+#endif
+#ifdef CONFIG_BIGNUM
+ else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
+ s->cur_func->js_mode |= JS_MODE_MATH;
+ }
+#endif
+ }
+ return js_parse_seek_token(s, &pos);
+}
+
+static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,
+ JSAtom func_name)
+{
+ JSAtom name;
+ int i, idx;
+
+ if (fd->js_mode & JS_MODE_STRICT) {
+ if (!fd->has_simple_parameter_list && fd->has_use_strict) {
+ return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");
+ }
+ if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) {
+ return js_parse_error(s, "invalid function name in strict code");
+ }
+ for (idx = 0; idx < fd->arg_count; idx++) {
+ name = fd->args[idx].var_name;
+
+ if (name == JS_ATOM_eval || name == JS_ATOM_arguments) {
+ return js_parse_error(s, "invalid argument name in strict code");
+ }
+ }
+ }
+ /* check async_generator case */
+ if ((fd->js_mode & JS_MODE_STRICT)
+ || !fd->has_simple_parameter_list
+ || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)
+ || fd->func_type == JS_PARSE_FUNC_ARROW
+ || fd->func_type == JS_PARSE_FUNC_METHOD) {
+ for (idx = 0; idx < fd->arg_count; idx++) {
+ name = fd->args[idx].var_name;
+ if (name != JS_ATOM_NULL) {
+ for (i = 0; i < idx; i++) {
+ if (fd->args[i].var_name == name)
+ goto duplicate;
+ }
+ /* Check if argument name duplicates a destructuring parameter */
+ /* XXX: should have a flag for such variables */
+ for (i = 0; i < fd->var_count; i++) {
+ if (fd->vars[i].var_name == name &&
+ fd->vars[i].scope_level == 0)
+ goto duplicate;
+ }
+ }
+ }
+ }
+ return 0;
+
+duplicate:
+ return js_parse_error(s, "duplicate argument names not allowed in this context");
+}
+
+/* create a function to initialize class fields */
+static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
+{
+ JSFunctionDef *fd;
+
+ fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,
+ s->filename, 0);
+ if (!fd)
+ return NULL;
+ fd->func_name = JS_ATOM_NULL;
+ fd->has_prototype = FALSE;
+ fd->has_home_object = TRUE;
+
+ fd->has_arguments_binding = FALSE;
+ fd->has_this_binding = TRUE;
+ fd->is_derived_class_constructor = FALSE;
+ fd->new_target_allowed = TRUE;
+ fd->super_call_allowed = FALSE;
+ fd->super_allowed = fd->has_home_object;
+ fd->arguments_allowed = FALSE;
+
+ fd->func_kind = JS_FUNC_NORMAL;
+ fd->func_type = JS_PARSE_FUNC_METHOD;
+ return fd;
+}
+
+/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
+ JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */
+static warn_unused int js_parse_function_decl2(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+ const uint8_t *ptr,
+ int function_line_num,
+ JSParseExportEnum export_flag,
+ JSFunctionDef **pfd)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionDef *fd = s->cur_func;
+ BOOL is_expr;
+ int func_idx, lexical_func_idx = -1;
+ BOOL has_opt_arg;
+ BOOL create_func_var = FALSE;
+
+ is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&
+ func_type != JS_PARSE_FUNC_VAR);
+
+ if (func_type == JS_PARSE_FUNC_STATEMENT ||
+ func_type == JS_PARSE_FUNC_VAR ||
+ func_type == JS_PARSE_FUNC_EXPR) {
+ if (func_kind == JS_FUNC_NORMAL &&
+ token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) != '\n') {
+ if (next_token(s))
+ return -1;
+ func_kind = JS_FUNC_ASYNC;
+ }
+ if (next_token(s))
+ return -1;
+ if (s->token.val == '*') {
+ if (next_token(s))
+ return -1;
+ func_kind |= JS_FUNC_GENERATOR;
+ }
+
+ if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved ||
+ (s->token.u.ident.atom == JS_ATOM_yield &&
+ func_type == JS_PARSE_FUNC_EXPR &&
+ (func_kind & JS_FUNC_GENERATOR)) ||
+ (s->token.u.ident.atom == JS_ATOM_await &&
+ func_type == JS_PARSE_FUNC_EXPR &&
+ (func_kind & JS_FUNC_ASYNC))) {
+ return js_parse_error_reserved_identifier(s);
+ }
+ }
+ if (s->token.val == TOK_IDENT ||
+ (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||
+ (s->token.val == TOK_AWAIT && !s->is_module)) &&
+ func_type == JS_PARSE_FUNC_EXPR)) {
+ func_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)) {
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ } else {
+ if (func_type != JS_PARSE_FUNC_EXPR &&
+ export_flag != JS_PARSE_EXPORT_DEFAULT) {
+ return js_parse_error(s, "function name expected");
+ }
+ }
+ } else if (func_type != JS_PARSE_FUNC_ARROW) {
+ func_name = JS_DupAtom(ctx, func_name);
+ }
+
+ if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&
+ (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {
+ JSGlobalVar *hf;
+ hf = find_global_var(fd, func_name);
+ /* XXX: should check scope chain */
+ if (hf && hf->scope_level == fd->scope_level) {
+ js_parse_error(s, "invalid redefinition of global identifier in module code");
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ }
+
+ if (func_type == JS_PARSE_FUNC_VAR) {
+ if (!(fd->js_mode & JS_MODE_STRICT)
+ && func_kind == JS_FUNC_NORMAL
+ && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0
+ && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))
+ && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {
+ create_func_var = TRUE;
+ }
+ /* Create the lexical name here so that the function closure
+ contains it */
+ if (fd->is_eval &&
+ (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
+ fd->eval_type == JS_EVAL_TYPE_MODULE) &&
+ fd->scope_level == fd->body_scope) {
+ /* avoid creating a lexical variable in the global
+ scope. XXX: check annex B */
+ JSGlobalVar *hf;
+ hf = find_global_var(fd, func_name);
+ /* XXX: should check scope chain */
+ if (hf && hf->scope_level == fd->scope_level) {
+ js_parse_error(s, "invalid redefinition of global identifier");
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ } else {
+ /* Always create a lexical name, fail if at the same scope as
+ existing name */
+ /* Lexical variable will be initialized upon entering scope */
+ lexical_func_idx = define_var(s, fd, func_name,
+ func_kind != JS_FUNC_NORMAL ?
+ JS_VAR_DEF_NEW_FUNCTION_DECL :
+ JS_VAR_DEF_FUNCTION_DECL);
+ if (lexical_func_idx < 0) {
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ }
+ }
+
+ fd = js_new_function_def(ctx, fd, FALSE, is_expr,
+ s->filename, function_line_num);
+ if (!fd) {
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ if (pfd)
+ *pfd = fd;
+ s->cur_func = fd;
+ fd->func_name = func_name;
+ /* XXX: test !fd->is_generator is always false */
+ fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||
+ func_type == JS_PARSE_FUNC_VAR ||
+ func_type == JS_PARSE_FUNC_EXPR) &&
+ func_kind == JS_FUNC_NORMAL;
+ fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||
+ func_type == JS_PARSE_FUNC_GETTER ||
+ func_type == JS_PARSE_FUNC_SETTER ||
+ func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
+ func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
+ fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);
+ fd->has_this_binding = fd->has_arguments_binding;
+ fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
+ if (func_type == JS_PARSE_FUNC_ARROW) {
+ fd->new_target_allowed = fd->parent->new_target_allowed;
+ fd->super_call_allowed = fd->parent->super_call_allowed;
+ fd->super_allowed = fd->parent->super_allowed;
+ fd->arguments_allowed = fd->parent->arguments_allowed;
+ } else {
+ fd->new_target_allowed = TRUE;
+ fd->super_call_allowed = fd->is_derived_class_constructor;
+ fd->super_allowed = fd->has_home_object;
+ fd->arguments_allowed = TRUE;
+ }
+
+ /* fd->in_function_body == FALSE prevents yield/await during the parsing
+ of the arguments in generator/async functions. They are parsed as
+ regular identifiers for other function kinds. */
+ fd->func_kind = func_kind;
+ fd->func_type = func_type;
+
+ if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
+ func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
+ /* error if not invoked as a constructor */
+ emit_op(s, OP_check_ctor);
+ }
+
+ if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
+ emit_class_field_init(s);
+ }
+
+ /* parse arguments */
+ fd->has_simple_parameter_list = TRUE;
+ fd->has_parameter_expressions = FALSE;
+ has_opt_arg = FALSE;
+ if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {
+ JSAtom name;
+ if (s->token.u.ident.is_reserved) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ name = s->token.u.ident.atom;
+ if (add_arg(ctx, fd, name) < 0)
+ goto fail;
+ fd->defined_arg_count = 1;
+ } else {
+ if (s->token.val == '(') {
+ int skip_bits;
+ /* if there is an '=' inside the parameter list, we
+ consider there is a parameter expression inside */
+ js_parse_skip_parens_token(s, &skip_bits, FALSE);
+ if (skip_bits & SKIP_HAS_ASSIGNMENT)
+ fd->has_parameter_expressions = TRUE;
+ if (next_token(s))
+ goto fail;
+ } else {
+ if (js_parse_expect(s, '('))
+ goto fail;
+ }
+
+ if (fd->has_parameter_expressions) {
+ fd->scope_level = -1; /* force no parent scope */
+ if (push_scope(s) < 0)
+ return -1;
+ }
+
+ while (s->token.val != ')') {
+ JSAtom name;
+ BOOL rest = FALSE;
+ int idx, has_initializer;
+
+ if (s->token.val == TOK_ELLIPSIS) {
+ fd->has_simple_parameter_list = FALSE;
+ rest = TRUE;
+ if (next_token(s))
+ goto fail;
+ }
+ if (s->token.val == '[' || s->token.val == '{') {
+ fd->has_simple_parameter_list = FALSE;
+ if (rest) {
+ emit_op(s, OP_rest);
+ emit_u16(s, fd->arg_count);
+ } else {
+ /* unnamed arg for destructuring */
+ idx = add_arg(ctx, fd, JS_ATOM_NULL);
+ emit_op(s, OP_get_arg);
+ emit_u16(s, idx);
+ }
+ has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE);
+ if (has_initializer < 0)
+ goto fail;
+ if (has_initializer)
+ has_opt_arg = TRUE;
+ if (!has_opt_arg)
+ fd->defined_arg_count++;
+ } else if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ name = s->token.u.ident.atom;
+ if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ if (fd->has_parameter_expressions) {
+ if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0)
+ goto fail;
+ }
+ /* XXX: could avoid allocating an argument if rest is true */
+ idx = add_arg(ctx, fd, name);
+ if (idx < 0)
+ goto fail;
+ if (next_token(s))
+ goto fail;
+ if (rest) {
+ emit_op(s, OP_rest);
+ emit_u16(s, idx);
+ if (fd->has_parameter_expressions) {
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ }
+ emit_op(s, OP_put_arg);
+ emit_u16(s, idx);
+ fd->has_simple_parameter_list = FALSE;
+ has_opt_arg = TRUE;
+ } else if (s->token.val == '=') {
+ int label;
+
+ fd->has_simple_parameter_list = FALSE;
+ has_opt_arg = TRUE;
+
+ if (next_token(s))
+ goto fail;
+
+ label = new_label(s);
+ emit_op(s, OP_get_arg);
+ emit_u16(s, idx);
+ emit_op(s, OP_dup);
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_strict_eq);
+ emit_goto(s, OP_if_false, label);
+ emit_op(s, OP_drop);
+ if (js_parse_assign_expr(s))
+ goto fail;
+ set_object_name(s, name);
+ emit_op(s, OP_dup);
+ emit_op(s, OP_put_arg);
+ emit_u16(s, idx);
+ emit_label(s, label);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ } else {
+ if (!has_opt_arg) {
+ fd->defined_arg_count++;
+ }
+ if (fd->has_parameter_expressions) {
+ /* copy the argument to the argument scope */
+ emit_op(s, OP_get_arg);
+ emit_u16(s, idx);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ }
+ }
+ } else {
+ js_parse_error(s, "missing formal parameter");
+ goto fail;
+ }
+ if (rest && s->token.val != ')') {
+ js_parse_expect(s, ')');
+ goto fail;
+ }
+ if (s->token.val == ')')
+ break;
+ if (js_parse_expect(s, ','))
+ goto fail;
+ }
+ if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||
+ (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {
+ js_parse_error(s, "invalid number of arguments for getter or setter");
+ goto fail;
+ }
+ }
+
+ if (fd->has_parameter_expressions) {
+ int idx;
+
+ /* Copy the variables in the argument scope to the variable
+ scope (see FunctionDeclarationInstantiation() in spec). The
+ normal arguments are already present, so no need to copy
+ them. */
+ idx = fd->scopes[fd->scope_level].first;
+ while (idx >= 0) {
+ JSVarDef *vd = &fd->vars[idx];
+ if (vd->scope_level != fd->scope_level)
+ break;
+ if (find_var(ctx, fd, vd->var_name) < 0) {
+ if (add_var(ctx, fd, vd->var_name) < 0)
+ goto fail;
+ vd = &fd->vars[idx]; /* fd->vars may have been reallocated */
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, vd->var_name);
+ emit_u16(s, fd->scope_level);
+ emit_op(s, OP_scope_put_var);
+ emit_atom(s, vd->var_name);
+ emit_u16(s, 0);
+ }
+ idx = vd->scope_next;
+ }
+
+ /* the argument scope has no parent, hence we don't use pop_scope(s) */
+ emit_op(s, OP_leave_scope);
+ emit_u16(s, fd->scope_level);
+
+ /* set the variable scope as the current scope */
+ fd->scope_level = 0;
+ fd->scope_first = fd->scopes[fd->scope_level].first;
+ }
+
+ if (next_token(s))
+ goto fail;
+
+ /* generator function: yield after the parameters are evaluated */
+ if (func_kind == JS_FUNC_GENERATOR ||
+ func_kind == JS_FUNC_ASYNC_GENERATOR)
+ emit_op(s, OP_initial_yield);
+
+ /* in generators, yield expression is forbidden during the parsing
+ of the arguments */
+ fd->in_function_body = TRUE;
+ push_scope(s); /* enter body scope */
+ fd->body_scope = fd->scope_level;
+
+ if (s->token.val == TOK_ARROW) {
+ if (next_token(s))
+ goto fail;
+
+ if (s->token.val != '{') {
+ if (js_parse_function_check_names(s, fd, func_name))
+ goto fail;
+
+ if (js_parse_assign_expr(s))
+ goto fail;
+
+ if (func_kind != JS_FUNC_NORMAL)
+ emit_op(s, OP_return_async);
+ else
+ emit_op(s, OP_return);
+
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ /* save the function source code */
+ /* the end of the function source code is after the last
+ token of the function source stored into s->last_ptr */
+ fd->source_len = s->last_ptr - ptr;
+ fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
+ if (!fd->source)
+ goto fail;
+ }
+ goto done;
+ }
+ }
+
+ if (js_parse_expect(s, '{'))
+ goto fail;
+
+ if (js_parse_directives(s))
+ goto fail;
+
+ /* in strict_mode, check function and argument names */
+ if (js_parse_function_check_names(s, fd, func_name))
+ goto fail;
+
+ while (s->token.val != '}') {
+ if (js_parse_source_element(s))
+ goto fail;
+ }
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ /* save the function source code */
+ fd->source_len = s->buf_ptr - ptr;
+ fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
+ if (!fd->source)
+ goto fail;
+ }
+
+ if (next_token(s)) {
+ /* consume the '}' */
+ goto fail;
+ }
+
+ /* in case there is no return, add one */
+ if (js_is_live_code(s)) {
+ emit_return(s, FALSE);
+ }
+done:
+ s->cur_func = fd->parent;
+
+ /* create the function object */
+ {
+ int idx;
+ JSAtom func_name = fd->func_name;
+
+ /* the real object will be set at the end of the compilation */
+ idx = cpool_add(s, JS_NULL);
+ fd->parent_cpool_idx = idx;
+
+ if (is_expr) {
+ /* for constructors, no code needs to be generated here */
+ if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&
+ func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
+ /* OP_fclosure creates the function object from the bytecode
+ and adds the scope information */
+ emit_op(s, OP_fclosure);
+ emit_u32(s, idx);
+ if (func_name == JS_ATOM_NULL) {
+ emit_op(s, OP_set_name);
+ emit_u32(s, JS_ATOM_NULL);
+ }
+ }
+ } else if (func_type == JS_PARSE_FUNC_VAR) {
+ emit_op(s, OP_fclosure);
+ emit_u32(s, idx);
+ if (create_func_var) {
+ if (s->cur_func->is_global_var) {
+ JSGlobalVar *hf;
+ /* the global variable must be defined at the start of the
+ function */
+ hf = add_global_var(ctx, s->cur_func, func_name);
+ if (!hf)
+ goto fail;
+ /* it is considered as defined at the top level
+ (needed for annex B.3.3.4 and B.3.3.5
+ checks) */
+ hf->scope_level = 0;
+ hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);
+ /* store directly into global var, bypass lexical scope */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var);
+ emit_atom(s, func_name);
+ emit_u16(s, 0);
+ } else {
+ /* do not call define_var to bypass lexical scope check */
+ func_idx = find_var(ctx, s->cur_func, func_name);
+ if (func_idx < 0) {
+ func_idx = add_var(ctx, s->cur_func, func_name);
+ if (func_idx < 0)
+ goto fail;
+ }
+ /* store directly into local var, bypass lexical catch scope */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var);
+ emit_atom(s, func_name);
+ emit_u16(s, 0);
+ }
+ }
+ if (lexical_func_idx >= 0) {
+ /* lexical variable will be initialized upon entering scope */
+ s->cur_func->vars[lexical_func_idx].func_pool_idx = idx;
+ emit_op(s, OP_drop);
+ } else {
+ /* store function object into its lexical name */
+ /* XXX: could use OP_put_loc directly */
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, func_name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+ } else {
+ if (!s->cur_func->is_global_var) {
+ int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);
+
+ if (var_idx < 0)
+ goto fail;
+ /* the variable will be assigned at the top of the function */
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx;
+ } else {
+ s->cur_func->vars[var_idx].func_pool_idx = idx;
+ }
+ } else {
+ JSAtom func_var_name;
+ JSGlobalVar *hf;
+ if (func_name == JS_ATOM_NULL)
+ func_var_name = JS_ATOM__default_; /* export default */
+ else
+ func_var_name = func_name;
+ /* the variable will be assigned at the top of the function */
+ hf = add_global_var(ctx, s->cur_func, func_var_name);
+ if (!hf)
+ goto fail;
+ hf->cpool_idx = idx;
+ if (export_flag != JS_PARSE_EXPORT_NONE) {
+ if (!add_export_entry(s, s->cur_func->module, func_var_name,
+ export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))
+ goto fail;
+ }
+ }
+ }
+ }
+ return 0;
+ fail:
+ s->cur_func = fd->parent;
+ js_free_function_def(ctx, fd);
+ if (pfd)
+ *pfd = NULL;
+ return -1;
+}
+
+static warn_unused int js_parse_function_decl(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+ const uint8_t *ptr,
+ int function_line_num)
+{
+ return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,
+ function_line_num, JS_PARSE_EXPORT_NONE,
+ NULL);
+}
+
+static warn_unused int js_parse_program(JSParseState *s)
+{
+ JSFunctionDef *fd = s->cur_func;
+ int idx;
+
+ if (next_token(s))
+ return -1;
+
+ if (js_parse_directives(s))
+ return -1;
+
+ fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||
+ (fd->eval_type == JS_EVAL_TYPE_MODULE) ||
+ !(fd->js_mode & JS_MODE_STRICT);
+
+ if (!s->is_module) {
+ /* hidden variable for the return value */
+ fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);
+ if (idx < 0)
+ return -1;
+ }
+
+ while (s->token.val != TOK_EOF) {
+ if (js_parse_source_element(s))
+ return -1;
+ }
+
+ if (!s->is_module) {
+ /* return the value of the hidden variable eval_ret_idx */
+ emit_op(s, OP_get_loc);
+ emit_u16(s, fd->eval_ret_idx);
+
+ emit_op(s, OP_return);
+ } else {
+ emit_op(s, OP_return_undef);
+ }
+
+ return 0;
+}
+
+static void js_parse_init(JSContext *ctx, JSParseState *s,
+ const char *input, size_t input_len,
+ const char *filename, int line)
+{
+ memset(s, 0, sizeof(*s));
+ s->ctx = ctx;
+ s->filename = filename;
+ s->line_num = line;
+ s->buf_ptr = (const uint8_t *)input;
+ s->buf_end = s->buf_ptr + input_len;
+ s->token.val = ' ';
+ s->token.line_num = 1;
+}
+
+static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
+ JSValueConst this_obj,
+ JSVarRef **var_refs, JSStackFrame *sf)
+{
+ JSValue ret_val;
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_TAG(fun_obj);
+ if (tag == JS_TAG_FUNCTION_BYTECODE) {
+ fun_obj = js_closure(ctx, fun_obj, var_refs, sf);
+ ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);
+ } else if (tag == JS_TAG_MODULE) {
+ JSModuleDef *m;
+ m = JS_VALUE_GET_PTR(fun_obj);
+ /* the module refcount should be >= 2 */
+ JS_FreeValue(ctx, fun_obj);
+ if (js_create_module_function(ctx, m) < 0)
+ goto fail;
+ if (js_link_module(ctx, m) < 0)
+ goto fail;
+ ret_val = js_evaluate_module(ctx, m);
+ if (JS_IsException(ret_val)) {
+ fail:
+ js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED);
+ return JS_EXCEPTION;
+ }
+ } else {
+ JS_FreeValue(ctx, fun_obj);
+ ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");
+ }
+ return ret_val;
+}
+
+JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
+{
+ return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
+}
+
+static void skip_shebang(JSParseState *s)
+{
+ const uint8_t *p = s->buf_ptr;
+ int c;
+
+ if (p[0] == '#' && p[1] == '!') {
+ p += 2;
+ while (p < s->buf_end) {
+ if (*p == '\n' || *p == '\r') {
+ break;
+ } else if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if (c == CP_LS || c == CP_PS) {
+ break;
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ s->buf_ptr = p;
+ }
+}
+
+/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
+static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int flags, int scope_idx)
+{
+ JSParseState s1, *s = &s1;
+ int err, js_mode, eval_type;
+ JSValue fun_obj, ret_val;
+ JSStackFrame *sf;
+ JSVarRef **var_refs;
+ JSFunctionBytecode *b;
+ JSFunctionDef *fd;
+ JSModuleDef *m;
+
+ js_parse_init(ctx, s, input, input_len, filename, line);
+ skip_shebang(s);
+
+ eval_type = flags & JS_EVAL_TYPE_MASK;
+ m = NULL;
+ if (eval_type == JS_EVAL_TYPE_DIRECT) {
+ JSObject *p;
+ sf = ctx->rt->current_stack_frame;
+ assert(sf != NULL);
+ assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ assert(js_class_has_bytecode(p->class_id));
+ b = p->u.func.function_bytecode;
+ var_refs = p->u.func.var_refs;
+ js_mode = b->js_mode;
+ } else {
+ sf = NULL;
+ b = NULL;
+ var_refs = NULL;
+ js_mode = 0;
+ if (flags & JS_EVAL_FLAG_STRICT)
+ js_mode |= JS_MODE_STRICT;
+ if (flags & JS_EVAL_FLAG_STRIP)
+ js_mode |= JS_MODE_STRIP;
+ if (eval_type == JS_EVAL_TYPE_MODULE) {
+ JSAtom module_name = JS_NewAtom(ctx, filename);
+ if (module_name == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+ m = js_new_module_def(ctx, module_name);
+ if (!m)
+ return JS_EXCEPTION;
+ js_mode |= JS_MODE_STRICT;
+ }
+ }
+ fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, line);
+ if (!fd)
+ goto fail1;
+ s->cur_func = fd;
+ fd->eval_type = eval_type;
+ fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
+ fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);
+ if (eval_type == JS_EVAL_TYPE_DIRECT) {
+ fd->new_target_allowed = b->new_target_allowed;
+ fd->super_call_allowed = b->super_call_allowed;
+ fd->super_allowed = b->super_allowed;
+ fd->arguments_allowed = b->arguments_allowed;
+ } else {
+ fd->new_target_allowed = FALSE;
+ fd->super_call_allowed = FALSE;
+ fd->super_allowed = FALSE;
+ fd->arguments_allowed = TRUE;
+ }
+ fd->js_mode = js_mode;
+ fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);
+ if (b) {
+ if (add_closure_variables(ctx, fd, b, scope_idx))
+ goto fail;
+ }
+ fd->module = m;
+ s->is_module = (m != NULL);
+ s->allow_html_comments = !s->is_module;
+
+ push_scope(s); /* body scope */
+ fd->body_scope = fd->scope_level;
+
+ err = js_parse_program(s);
+ if (err) {
+ fail:
+ free_token(s, &s->token);
+ js_free_function_def(ctx, fd);
+ goto fail1;
+ }
+
+ /* create the function object and all the enclosed functions */
+ fun_obj = js_create_function(ctx, fd);
+ if (JS_IsException(fun_obj))
+ goto fail1;
+ /* Could add a flag to avoid resolution if necessary */
+ if (m) {
+ m->func_obj = fun_obj;
+ if (js_resolve_module(ctx, m) < 0)
+ goto fail1;
+ fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ }
+ if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
+ ret_val = fun_obj;
+ } else {
+ ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);
+ }
+ return ret_val;
+ fail1:
+ /* XXX: should free all the unresolved dependencies */
+ if (m)
+ js_free_module_def(ctx, m);
+ return JS_EXCEPTION;
+}
+
+/* the indirection is needed to make 'eval' optional */
+static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int flags, int scope_idx)
+{
+ if (unlikely(!ctx->eval_internal)) {
+ return JS_ThrowTypeError(ctx, "eval is not supported");
+ }
+ return ctx->eval_internal(ctx, this_obj, input, input_len, filename, line,
+ flags, scope_idx);
+}
+
+static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+ JSValueConst val, int flags, int scope_idx)
+{
+ JSValue ret;
+ const char *str;
+ size_t len;
+
+ if (!JS_IsString(val))
+ return JS_DupValue(ctx, val);
+ str = JS_ToCStringLen(ctx, &len, val);
+ if (!str)
+ return JS_EXCEPTION;
+ ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", 1, flags, scope_idx);
+ JS_FreeCString(ctx, str);
+ return ret;
+
+}
+
+JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int eval_flags)
+{
+ int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
+ JSValue ret;
+
+ assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
+ eval_type == JS_EVAL_TYPE_MODULE);
+ ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, line,
+ eval_flags, -1);
+ return ret;
+}
+
+JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
+ const char *filename, int eval_flags)
+{
+ return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, 1,
+ eval_flags);
+}
+
+int JS_ResolveModule(JSContext *ctx, JSValueConst obj)
+{
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
+ JSModuleDef *m = JS_VALUE_GET_PTR(obj);
+ if (js_resolve_module(ctx, m) < 0) {
+ js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*******************************************************************/
+/* object list */
+
+typedef struct {
+ JSObject *obj;
+ uint32_t hash_next; /* -1 if no next entry */
+} JSObjectListEntry;
+
+/* XXX: reuse it to optimize weak references */
+typedef struct {
+ JSObjectListEntry *object_tab;
+ int object_count;
+ int object_size;
+ uint32_t *hash_table;
+ uint32_t hash_size;
+} JSObjectList;
+
+static void js_object_list_init(JSObjectList *s)
+{
+ memset(s, 0, sizeof(*s));
+}
+
+static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
+{
+ return ((uintptr_t)p * 3163) & (hash_size - 1);
+}
+
+static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
+ uint32_t new_hash_size)
+{
+ JSObjectListEntry *e;
+ uint32_t i, h, *new_hash_table;
+
+ new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
+ if (!new_hash_table)
+ return -1;
+ js_free(ctx, s->hash_table);
+ s->hash_table = new_hash_table;
+ s->hash_size = new_hash_size;
+
+ for(i = 0; i < s->hash_size; i++) {
+ s->hash_table[i] = -1;
+ }
+ for(i = 0; i < s->object_count; i++) {
+ e = &s->object_tab[i];
+ h = js_object_list_get_hash(e->obj, s->hash_size);
+ e->hash_next = s->hash_table[h];
+ s->hash_table[h] = i;
+ }
+ return 0;
+}
+
+/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
+ memory error */
+static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
+{
+ JSObjectListEntry *e;
+ uint32_t h, new_hash_size;
+
+ if (js_resize_array(ctx, (void *)&s->object_tab,
+ sizeof(s->object_tab[0]),
+ &s->object_size, s->object_count + 1))
+ return -1;
+ if (unlikely((s->object_count + 1) >= s->hash_size)) {
+ new_hash_size = max_uint32(s->hash_size, 4);
+ while (new_hash_size <= s->object_count)
+ new_hash_size *= 2;
+ if (js_object_list_resize_hash(ctx, s, new_hash_size))
+ return -1;
+ }
+ e = &s->object_tab[s->object_count++];
+ h = js_object_list_get_hash(obj, s->hash_size);
+ e->obj = obj;
+ e->hash_next = s->hash_table[h];
+ s->hash_table[h] = s->object_count - 1;
+ return 0;
+}
+
+/* return -1 if not present or the object index */
+static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
+{
+ JSObjectListEntry *e;
+ uint32_t h, p;
+
+ /* must test empty size because there is no hash table */
+ if (s->object_count == 0)
+ return -1;
+ h = js_object_list_get_hash(obj, s->hash_size);
+ p = s->hash_table[h];
+ while (p != -1) {
+ e = &s->object_tab[p];
+ if (e->obj == obj)
+ return p;
+ p = e->hash_next;
+ }
+ return -1;
+}
+
+static void js_object_list_end(JSContext *ctx, JSObjectList *s)
+{
+ js_free(ctx, s->object_tab);
+ js_free(ctx, s->hash_table);
+}
+
+/*******************************************************************/
+/* binary object writer & reader */
+
+typedef enum BCTagEnum {
+ BC_TAG_NULL = 1,
+ BC_TAG_UNDEFINED,
+ BC_TAG_BOOL_FALSE,
+ BC_TAG_BOOL_TRUE,
+ BC_TAG_INT32,
+ BC_TAG_FLOAT64,
+ BC_TAG_STRING,
+ BC_TAG_OBJECT,
+ BC_TAG_ARRAY,
+ BC_TAG_BIG_INT,
+ BC_TAG_BIG_FLOAT,
+ BC_TAG_BIG_DECIMAL,
+ BC_TAG_TEMPLATE_OBJECT,
+ BC_TAG_FUNCTION_BYTECODE,
+ BC_TAG_MODULE,
+ BC_TAG_TYPED_ARRAY,
+ BC_TAG_ARRAY_BUFFER,
+ BC_TAG_SHARED_ARRAY_BUFFER,
+ BC_TAG_DATE,
+ BC_TAG_OBJECT_VALUE,
+ BC_TAG_OBJECT_REFERENCE,
+} BCTagEnum;
+
+#ifdef CONFIG_BIGNUM
+#define BC_BASE_VERSION 2
+#else
+#define BC_BASE_VERSION 1
+#endif
+#define BC_BE_VERSION 0x40
+#ifdef WORDS_BIGENDIAN
+#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)
+#else
+#define BC_VERSION BC_BASE_VERSION
+#endif
+
+typedef struct BCWriterState {
+ JSContext *ctx;
+ DynBuf dbuf;
+ BOOL byte_swap : 8;
+ BOOL allow_bytecode : 8;
+ BOOL allow_sab : 8;
+ BOOL allow_reference : 8;
+ uint32_t first_atom;
+ uint32_t *atom_to_idx;
+ int atom_to_idx_size;
+ JSAtom *idx_to_atom;
+ int idx_to_atom_count;
+ int idx_to_atom_size;
+ uint8_t **sab_tab;
+ int sab_tab_len;
+ int sab_tab_size;
+ /* list of referenced objects (used if allow_reference = TRUE) */
+ JSObjectList object_list;
+} BCWriterState;
+
+#ifdef DUMP_READ_OBJECT
+static const char * const bc_tag_str[] = {
+ "invalid",
+ "null",
+ "undefined",
+ "false",
+ "true",
+ "int32",
+ "float64",
+ "string",
+ "object",
+ "array",
+ "bigint",
+ "bigfloat",
+ "bigdecimal",
+ "template",
+ "function",
+ "module",
+ "TypedArray",
+ "ArrayBuffer",
+ "SharedArrayBuffer",
+ "Date",
+ "ObjectValue",
+ "ObjectReference",
+};
+#endif
+
+static void bc_put_u8(BCWriterState *s, uint8_t v)
+{
+ dbuf_putc(&s->dbuf, v);
+}
+
+static void bc_put_u16(BCWriterState *s, uint16_t v)
+{
+ if (s->byte_swap)
+ v = bswap16(v);
+ dbuf_put_u16(&s->dbuf, v);
+}
+
+static maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
+{
+ if (s->byte_swap)
+ v = bswap32(v);
+ dbuf_put_u32(&s->dbuf, v);
+}
+
+static void bc_put_u64(BCWriterState *s, uint64_t v)
+{
+ if (s->byte_swap)
+ v = bswap64(v);
+ dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
+}
+
+static void bc_put_leb128(BCWriterState *s, uint32_t v)
+{
+ dbuf_put_leb128(&s->dbuf, v);
+}
+
+static void bc_put_sleb128(BCWriterState *s, int32_t v)
+{
+ dbuf_put_sleb128(&s->dbuf, v);
+}
+
+static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)
+{
+ *pflags = *pflags | (val << *pidx);
+ *pidx += n;
+}
+
+static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
+{
+ uint32_t v;
+
+ if (atom < s->first_atom || JS_AtomIsTaggedInt(atom)) {
+ *pres = atom;
+ return 0;
+ }
+ atom -= s->first_atom;
+ if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {
+ *pres = s->atom_to_idx[atom];
+ return 0;
+ }
+ if (atom >= s->atom_to_idx_size) {
+ int old_size, i;
+ old_size = s->atom_to_idx_size;
+ if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
+ sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
+ atom + 1))
+ return -1;
+ /* XXX: could add a specific js_resize_array() function to do it */
+ for(i = old_size; i < s->atom_to_idx_size; i++)
+ s->atom_to_idx[i] = 0;
+ }
+ if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
+ sizeof(s->idx_to_atom[0]),
+ &s->idx_to_atom_size, s->idx_to_atom_count + 1))
+ goto fail;
+
+ v = s->idx_to_atom_count++;
+ s->idx_to_atom[v] = atom + s->first_atom;
+ v += s->first_atom;
+ s->atom_to_idx[atom] = v;
+ *pres = v;
+ return 0;
+ fail:
+ *pres = 0;
+ return -1;
+}
+
+static int bc_put_atom(BCWriterState *s, JSAtom atom)
+{
+ uint32_t v;
+
+ if (JS_AtomIsTaggedInt(atom)) {
+ v = (JS_AtomToUInt32(atom) << 1) | 1;
+ } else {
+ if (bc_atom_to_idx(s, &v, atom))
+ return -1;
+ v <<= 1;
+ }
+ bc_put_leb128(s, v);
+ return 0;
+}
+
+static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
+{
+ int pos, len, op, fmt;
+
+ pos = 0;
+ while (pos < bc_len) {
+ op = bc_buf[pos];
+ len = short_opcode_info(op).size;
+ fmt = short_opcode_info(op).fmt;
+ switch(fmt) {
+ case OP_FMT_u16:
+ case OP_FMT_i16:
+ case OP_FMT_label16:
+ case OP_FMT_npop:
+ case OP_FMT_loc:
+ case OP_FMT_arg:
+ case OP_FMT_var_ref:
+ put_u16(bc_buf + pos + 1,
+ bswap16(get_u16(bc_buf + pos + 1)));
+ break;
+ case OP_FMT_i32:
+ case OP_FMT_u32:
+ case OP_FMT_const:
+ case OP_FMT_label:
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ put_u32(bc_buf + pos + 1,
+ bswap32(get_u32(bc_buf + pos + 1)));
+ break;
+ case OP_FMT_atom_u16:
+ case OP_FMT_label_u16:
+ put_u32(bc_buf + pos + 1,
+ bswap32(get_u32(bc_buf + pos + 1)));
+ put_u16(bc_buf + pos + 1 + 4,
+ bswap16(get_u16(bc_buf + pos + 1 + 4)));
+ break;
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ put_u32(bc_buf + pos + 1,
+ bswap32(get_u32(bc_buf + pos + 1)));
+ put_u32(bc_buf + pos + 1 + 4,
+ bswap32(get_u32(bc_buf + pos + 1 + 4)));
+ if (fmt == OP_FMT_atom_label_u16) {
+ put_u16(bc_buf + pos + 1 + 4 + 4,
+ bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));
+ }
+ break;
+ case OP_FMT_npop_u16:
+ put_u16(bc_buf + pos + 1,
+ bswap16(get_u16(bc_buf + pos + 1)));
+ put_u16(bc_buf + pos + 1 + 2,
+ bswap16(get_u16(bc_buf + pos + 1 + 2)));
+ break;
+ default:
+ break;
+ }
+ pos += len;
+ }
+}
+
+static int JS_WriteFunctionBytecode(BCWriterState *s,
+ const uint8_t *bc_buf1, int bc_len)
+{
+ int pos, len, op;
+ JSAtom atom;
+ uint8_t *bc_buf;
+ uint32_t val;
+
+ bc_buf = js_malloc(s->ctx, bc_len);
+ if (!bc_buf)
+ return -1;
+ memcpy(bc_buf, bc_buf1, bc_len);
+
+ pos = 0;
+ while (pos < bc_len) {
+ op = bc_buf[pos];
+ len = short_opcode_info(op).size;
+ switch(short_opcode_info(op).fmt) {
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ case OP_FMT_atom_u16:
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ atom = get_u32(bc_buf + pos + 1);
+ if (bc_atom_to_idx(s, &val, atom))
+ goto fail;
+ put_u32(bc_buf + pos + 1, val);
+ break;
+ default:
+ break;
+ }
+ pos += len;
+ }
+
+ if (s->byte_swap)
+ bc_byte_swap(bc_buf, bc_len);
+
+ dbuf_put(&s->dbuf, bc_buf, bc_len);
+
+ js_free(s->ctx, bc_buf);
+ return 0;
+ fail:
+ js_free(s->ctx, bc_buf);
+ return -1;
+}
+
+static void JS_WriteString(BCWriterState *s, JSString *p)
+{
+ int i;
+ bc_put_leb128(s, (p->len << 1) | p->is_wide_char);
+ if (p->is_wide_char) {
+ for(i = 0; i < p->len; i++)
+ bc_put_u16(s, p->u.str16[i]);
+ } else {
+ dbuf_put(&s->dbuf, p->u.str8, p->len);
+ }
+}
+
+#ifdef CONFIG_BIGNUM
+static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
+{
+ uint32_t tag, tag1;
+ int64_t e;
+ JSBigFloat *bf = JS_VALUE_GET_PTR(obj);
+ bf_t *a = &bf->num;
+ size_t len, i, n1, j;
+ limb_t v;
+
+ tag = JS_VALUE_GET_TAG(obj);
+ switch(tag) {
+ case JS_TAG_BIG_INT:
+ tag1 = BC_TAG_BIG_INT;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ tag1 = BC_TAG_BIG_FLOAT;
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ tag1 = BC_TAG_BIG_DECIMAL;
+ break;
+ default:
+ abort();
+ }
+ bc_put_u8(s, tag1);
+
+ /* sign + exponent */
+ if (a->expn == BF_EXP_ZERO)
+ e = 0;
+ else if (a->expn == BF_EXP_INF)
+ e = 1;
+ else if (a->expn == BF_EXP_NAN)
+ e = 2;
+ else if (a->expn >= 0)
+ e = a->expn + 3;
+ else
+ e = a->expn;
+ e = (e << 1) | a->sign;
+ if (e < INT32_MIN || e > INT32_MAX) {
+ JS_ThrowInternalError(s->ctx, "bignum exponent is too large");
+ return -1;
+ }
+ bc_put_sleb128(s, e);
+
+ /* mantissa */
+ if (a->len != 0) {
+ if (tag != JS_TAG_BIG_DECIMAL) {
+ i = 0;
+ while (i < a->len && a->tab[i] == 0)
+ i++;
+ assert(i < a->len);
+ v = a->tab[i];
+ n1 = sizeof(limb_t);
+ while ((v & 0xff) == 0) {
+ n1--;
+ v >>= 8;
+ }
+ i++;
+ len = (a->len - i) * sizeof(limb_t) + n1;
+ if (len > INT32_MAX) {
+ JS_ThrowInternalError(s->ctx, "bignum is too large");
+ return -1;
+ }
+ bc_put_leb128(s, len);
+ /* always saved in byte based little endian representation */
+ for(j = 0; j < n1; j++) {
+ dbuf_putc(&s->dbuf, v >> (j * 8));
+ }
+ for(; i < a->len; i++) {
+ limb_t v = a->tab[i];
+#if LIMB_BITS == 32
+#ifdef WORDS_BIGENDIAN
+ v = bswap32(v);
+#endif
+ dbuf_put_u32(&s->dbuf, v);
+#else
+#ifdef WORDS_BIGENDIAN
+ v = bswap64(v);
+#endif
+ dbuf_put_u64(&s->dbuf, v);
+#endif
+ }
+ } else {
+ int bpos, d;
+ uint8_t v8;
+ size_t i0;
+
+ /* little endian BCD */
+ i = 0;
+ while (i < a->len && a->tab[i] == 0)
+ i++;
+ assert(i < a->len);
+ len = a->len * LIMB_DIGITS;
+ v = a->tab[i];
+ j = 0;
+ while ((v % 10) == 0) {
+ j++;
+ v /= 10;
+ }
+ len -= j;
+ assert(len > 0);
+ if (len > INT32_MAX) {
+ JS_ThrowInternalError(s->ctx, "bignum is too large");
+ return -1;
+ }
+ bc_put_leb128(s, len);
+
+ bpos = 0;
+ v8 = 0;
+ i0 = i;
+ for(; i < a->len; i++) {
+ if (i != i0) {
+ v = a->tab[i];
+ j = 0;
+ }
+ for(; j < LIMB_DIGITS; j++) {
+ d = v % 10;
+ v /= 10;
+ if (bpos == 0) {
+ v8 = d;
+ bpos = 1;
+ } else {
+ dbuf_putc(&s->dbuf, v8 | (d << 4));
+ bpos = 0;
+ }
+ }
+ }
+ /* flush the last digit */
+ if (bpos) {
+ dbuf_putc(&s->dbuf, v8);
+ }
+ }
+ }
+ return 0;
+}
+#endif /* CONFIG_BIGNUM */
+
+static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
+
+static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
+{
+ JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
+ uint32_t flags;
+ int idx, i;
+
+ bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, b->has_prototype, 1);
+ bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
+ bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
+ bc_set_flags(&flags, &idx, b->need_home_object, 1);
+ bc_set_flags(&flags, &idx, b->func_kind, 2);
+ bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
+ bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
+ bc_set_flags(&flags, &idx, b->super_allowed, 1);
+ bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
+ bc_set_flags(&flags, &idx, b->has_debug, 1);
+ bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
+ assert(idx <= 16);
+ bc_put_u16(s, flags);
+ bc_put_u8(s, b->js_mode);
+ bc_put_atom(s, b->func_name);
+
+ bc_put_leb128(s, b->arg_count);
+ bc_put_leb128(s, b->var_count);
+ bc_put_leb128(s, b->defined_arg_count);
+ bc_put_leb128(s, b->stack_size);
+ bc_put_leb128(s, b->closure_var_count);
+ bc_put_leb128(s, b->cpool_count);
+ bc_put_leb128(s, b->byte_code_len);
+ if (b->vardefs) {
+ /* XXX: this field is redundant */
+ bc_put_leb128(s, b->arg_count + b->var_count);
+ for(i = 0; i < b->arg_count + b->var_count; i++) {
+ JSVarDef *vd = &b->vardefs[i];
+ bc_put_atom(s, vd->var_name);
+ bc_put_leb128(s, vd->scope_level);
+ bc_put_leb128(s, vd->scope_next + 1);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, vd->var_kind, 4);
+ bc_set_flags(&flags, &idx, vd->is_const, 1);
+ bc_set_flags(&flags, &idx, vd->is_lexical, 1);
+ bc_set_flags(&flags, &idx, vd->is_captured, 1);
+ assert(idx <= 8);
+ bc_put_u8(s, flags);
+ }
+ } else {
+ bc_put_leb128(s, 0);
+ }
+
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ bc_put_atom(s, cv->var_name);
+ bc_put_leb128(s, cv->var_idx);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, cv->is_local, 1);
+ bc_set_flags(&flags, &idx, cv->is_arg, 1);
+ bc_set_flags(&flags, &idx, cv->is_const, 1);
+ bc_set_flags(&flags, &idx, cv->is_lexical, 1);
+ bc_set_flags(&flags, &idx, cv->var_kind, 4);
+ assert(idx <= 8);
+ bc_put_u8(s, flags);
+ }
+
+ if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
+ goto fail;
+
+ if (b->has_debug) {
+ bc_put_atom(s, b->debug.filename);
+ bc_put_leb128(s, b->debug.line_num);
+ bc_put_leb128(s, b->debug.pc2line_len);
+ dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
+ }
+
+ for(i = 0; i < b->cpool_count; i++) {
+ if (JS_WriteObjectRec(s, b->cpool[i]))
+ goto fail;
+ }
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
+{
+ JSModuleDef *m = JS_VALUE_GET_PTR(obj);
+ int i;
+
+ bc_put_u8(s, BC_TAG_MODULE);
+ bc_put_atom(s, m->module_name);
+
+ bc_put_leb128(s, m->req_module_entries_count);
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ bc_put_atom(s, rme->module_name);
+ }
+
+ bc_put_leb128(s, m->export_entries_count);
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ bc_put_u8(s, me->export_type);
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ bc_put_leb128(s, me->u.local.var_idx);
+ } else {
+ bc_put_leb128(s, me->u.req_module_idx);
+ bc_put_atom(s, me->local_name);
+ }
+ bc_put_atom(s, me->export_name);
+ }
+
+ bc_put_leb128(s, m->star_export_entries_count);
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ bc_put_leb128(s, se->req_module_idx);
+ }
+
+ bc_put_leb128(s, m->import_entries_count);
+ for(i = 0; i < m->import_entries_count; i++) {
+ JSImportEntry *mi = &m->import_entries[i];
+ bc_put_leb128(s, mi->var_idx);
+ bc_put_atom(s, mi->import_name);
+ bc_put_leb128(s, mi->req_module_idx);
+ }
+
+ if (JS_WriteObjectRec(s, m->func_obj))
+ goto fail;
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ uint32_t i, len;
+ JSValue val;
+ int ret;
+ BOOL is_template;
+
+ if (s->allow_bytecode && !p->extensible) {
+ /* not extensible array: we consider it is a
+ template when we are saving bytecode */
+ bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
+ is_template = TRUE;
+ } else {
+ bc_put_u8(s, BC_TAG_ARRAY);
+ is_template = FALSE;
+ }
+ if (js_get_length32(s->ctx, &len, obj))
+ goto fail1;
+ bc_put_leb128(s, len);
+ for(i = 0; i < len; i++) {
+ val = JS_GetPropertyUint32(s->ctx, obj, i);
+ if (JS_IsException(val))
+ goto fail1;
+ ret = JS_WriteObjectRec(s, val);
+ JS_FreeValue(s->ctx, val);
+ if (ret)
+ goto fail1;
+ }
+ if (is_template) {
+ val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
+ if (JS_IsException(val))
+ goto fail1;
+ ret = JS_WriteObjectRec(s, val);
+ JS_FreeValue(s->ctx, val);
+ if (ret)
+ goto fail1;
+ }
+ return 0;
+ fail1:
+ return -1;
+}
+
+static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ uint32_t i, prop_count;
+ JSShape *sh;
+ JSShapeProperty *pr;
+ int pass;
+ JSAtom atom;
+
+ bc_put_u8(s, BC_TAG_OBJECT);
+ prop_count = 0;
+ sh = p->shape;
+ for(pass = 0; pass < 2; pass++) {
+ if (pass == 1)
+ bc_put_leb128(s, prop_count);
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
+ atom = pr->atom;
+ if (atom != JS_ATOM_NULL &&
+ JS_AtomIsString(s->ctx, atom) &&
+ (pr->flags & JS_PROP_ENUMERABLE)) {
+ if (pr->flags & JS_PROP_TMASK) {
+ JS_ThrowTypeError(s->ctx, "only value properties are supported");
+ goto fail;
+ }
+ if (pass == 0) {
+ prop_count++;
+ } else {
+ bc_put_atom(s, atom);
+ if (JS_WriteObjectRec(s, p->prop[i].u.value))
+ goto fail;
+ }
+ }
+ }
+ }
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSTypedArray *ta = p->u.typed_array;
+
+ bc_put_u8(s, BC_TAG_TYPED_ARRAY);
+ bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
+ bc_put_leb128(s, p->u.array.count);
+ bc_put_leb128(s, ta->offset);
+ if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
+ return -1;
+ return 0;
+}
+
+static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
+ return -1;
+ }
+ bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
+ bc_put_leb128(s, abuf->byte_length);
+ dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
+ return 0;
+}
+
+static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ assert(!abuf->detached); /* SharedArrayBuffer are never detached */
+ bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
+ bc_put_leb128(s, abuf->byte_length);
+ bc_put_u64(s, (uintptr_t)abuf->data);
+ if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
+ &s->sab_tab_size, s->sab_tab_len + 1))
+ return -1;
+ /* keep the SAB pointer so that the user can clone it or free it */
+ s->sab_tab[s->sab_tab_len++] = abuf->data;
+ return 0;
+}
+
+static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
+{
+ uint32_t tag;
+
+ if (js_check_stack_overflow(s->ctx->rt, 0)) {
+ JS_ThrowStackOverflow(s->ctx);
+ return -1;
+ }
+
+ tag = JS_VALUE_GET_NORM_TAG(obj);
+ switch(tag) {
+ case JS_TAG_NULL:
+ bc_put_u8(s, BC_TAG_NULL);
+ break;
+ case JS_TAG_UNDEFINED:
+ bc_put_u8(s, BC_TAG_UNDEFINED);
+ break;
+ case JS_TAG_BOOL:
+ bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));
+ break;
+ case JS_TAG_INT:
+ bc_put_u8(s, BC_TAG_INT32);
+ bc_put_sleb128(s, JS_VALUE_GET_INT(obj));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ bc_put_u8(s, BC_TAG_FLOAT64);
+ u.d = JS_VALUE_GET_FLOAT64(obj);
+ bc_put_u64(s, u.u64);
+ }
+ break;
+ case JS_TAG_STRING:
+ {
+ JSString *p = JS_VALUE_GET_STRING(obj);
+ bc_put_u8(s, BC_TAG_STRING);
+ JS_WriteString(s, p);
+ }
+ break;
+ case JS_TAG_FUNCTION_BYTECODE:
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ if (JS_WriteFunctionTag(s, obj))
+ goto fail;
+ break;
+ case JS_TAG_MODULE:
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ if (JS_WriteModule(s, obj))
+ goto fail;
+ break;
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ int ret, idx;
+
+ if (s->allow_reference) {
+ idx = js_object_list_find(s->ctx, &s->object_list, p);
+ if (idx >= 0) {
+ bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
+ bc_put_leb128(s, idx);
+ break;
+ } else {
+ if (js_object_list_add(s->ctx, &s->object_list, p))
+ goto fail;
+ }
+ } else {
+ if (p->tmp_mark) {
+ JS_ThrowTypeError(s->ctx, "circular reference");
+ goto fail;
+ }
+ p->tmp_mark = 1;
+ }
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY:
+ ret = JS_WriteArray(s, obj);
+ break;
+ case JS_CLASS_OBJECT:
+ ret = JS_WriteObjectTag(s, obj);
+ break;
+ case JS_CLASS_ARRAY_BUFFER:
+ ret = JS_WriteArrayBuffer(s, obj);
+ break;
+ case JS_CLASS_SHARED_ARRAY_BUFFER:
+ if (!s->allow_sab)
+ goto invalid_tag;
+ ret = JS_WriteSharedArrayBuffer(s, obj);
+ break;
+ case JS_CLASS_DATE:
+ bc_put_u8(s, BC_TAG_DATE);
+ ret = JS_WriteObjectRec(s, p->u.object_data);
+ break;
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ bc_put_u8(s, BC_TAG_OBJECT_VALUE);
+ ret = JS_WriteObjectRec(s, p->u.object_data);
+ break;
+ default:
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_WriteTypedArray(s, obj);
+ } else {
+ JS_ThrowTypeError(s->ctx, "unsupported object class");
+ ret = -1;
+ }
+ break;
+ }
+ p->tmp_mark = 0;
+ if (ret)
+ goto fail;
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ case JS_TAG_BIG_DECIMAL:
+ if (JS_WriteBigNum(s, obj))
+ goto fail;
+ break;
+#endif
+ default:
+ invalid_tag:
+ JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);
+ goto fail;
+ }
+ return 0;
+
+ fail:
+ return -1;
+}
+
+/* create the atom table */
+static int JS_WriteObjectAtoms(BCWriterState *s)
+{
+ JSRuntime *rt = s->ctx->rt;
+ DynBuf dbuf1;
+ int i, atoms_size;
+ uint8_t version;
+
+ dbuf1 = s->dbuf;
+ js_dbuf_init(s->ctx, &s->dbuf);
+
+ version = BC_VERSION;
+ if (s->byte_swap)
+ version ^= BC_BE_VERSION;
+ bc_put_u8(s, version);
+
+ bc_put_leb128(s, s->idx_to_atom_count);
+ for(i = 0; i < s->idx_to_atom_count; i++) {
+ JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];
+ JS_WriteString(s, p);
+ }
+ /* XXX: should check for OOM in above phase */
+
+ /* move the atoms at the start */
+ /* XXX: could just append dbuf1 data, but it uses more memory if
+ dbuf1 is larger than dbuf */
+ atoms_size = s->dbuf.size;
+ if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))
+ goto fail;
+ memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);
+ memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);
+ dbuf1.size += atoms_size;
+ dbuf_free(&s->dbuf);
+ s->dbuf = dbuf1;
+ return 0;
+ fail:
+ dbuf_free(&dbuf1);
+ return -1;
+}
+
+uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags, uint8_t ***psab_tab, size_t *psab_tab_len)
+{
+ BCWriterState ss, *s = &ss;
+
+ memset(s, 0, sizeof(*s));
+ s->ctx = ctx;
+ /* XXX: byte swapped output is untested */
+ s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);
+ s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
+ s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
+ s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
+ /* XXX: could use a different version when bytecode is included */
+ if (s->allow_bytecode)
+ s->first_atom = JS_ATOM_END;
+ else
+ s->first_atom = 1;
+ js_dbuf_init(ctx, &s->dbuf);
+ js_object_list_init(&s->object_list);
+
+ if (JS_WriteObjectRec(s, obj))
+ goto fail;
+ if (JS_WriteObjectAtoms(s))
+ goto fail;
+ js_object_list_end(ctx, &s->object_list);
+ js_free(ctx, s->atom_to_idx);
+ js_free(ctx, s->idx_to_atom);
+ *psize = s->dbuf.size;
+ if (psab_tab)
+ *psab_tab = s->sab_tab;
+ if (psab_tab_len)
+ *psab_tab_len = s->sab_tab_len;
+ return s->dbuf.buf;
+ fail:
+ js_object_list_end(ctx, &s->object_list);
+ js_free(ctx, s->atom_to_idx);
+ js_free(ctx, s->idx_to_atom);
+ dbuf_free(&s->dbuf);
+ *psize = 0;
+ if (psab_tab)
+ *psab_tab = NULL;
+ if (psab_tab_len)
+ *psab_tab_len = 0;
+ return NULL;
+}
+
+uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags)
+{
+ return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
+}
+
+typedef struct BCReaderState {
+ JSContext *ctx;
+ const uint8_t *buf_start, *ptr, *buf_end;
+ uint32_t first_atom;
+ uint32_t idx_to_atom_count;
+ JSAtom *idx_to_atom;
+ int error_state;
+ BOOL allow_sab : 8;
+ BOOL allow_bytecode : 8;
+ BOOL is_rom_data : 8;
+ BOOL allow_reference : 8;
+ /* object references */
+ JSObject **objects;
+ int objects_count;
+ int objects_size;
+
+#ifdef DUMP_READ_OBJECT
+ const uint8_t *ptr_last;
+ int level;
+#endif
+} BCReaderState;
+
+#ifdef DUMP_READ_OBJECT
+static void FORMAT_ATTR(2, 3) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
+ va_list ap;
+ int i, n, n0;
+
+ if (!s->ptr_last)
+ s->ptr_last = s->buf_start;
+
+ n = n0 = 0;
+ if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {
+ n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));
+ n += n0;
+ }
+ for (i = 0; s->ptr_last < s->ptr; i++) {
+ if ((i & 7) == 0 && i > 0) {
+ printf("\n%*s", n0, "");
+ n = n0;
+ }
+ n += printf(" %02x", *s->ptr_last++);
+ }
+ if (*fmt == '}')
+ s->level--;
+ if (n < 32 + s->level * 2) {
+ printf("%*s", 32 + s->level * 2 - n, "");
+ }
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ if (strchr(fmt, '{'))
+ s->level++;
+}
+#else
+#define bc_read_trace(...)
+#endif
+
+static int bc_read_error_end(BCReaderState *s)
+{
+ if (!s->error_state) {
+ JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");
+ }
+ return s->error_state = -1;
+}
+
+static int bc_get_u8(BCReaderState *s, uint8_t *pval)
+{
+ if (unlikely(s->buf_end - s->ptr < 1)) {
+ *pval = 0; /* avoid warning */
+ return bc_read_error_end(s);
+ }
+ *pval = *s->ptr++;
+ return 0;
+}
+
+static int bc_get_u16(BCReaderState *s, uint16_t *pval)
+{
+ if (unlikely(s->buf_end - s->ptr < 2)) {
+ *pval = 0; /* avoid warning */
+ return bc_read_error_end(s);
+ }
+ *pval = get_u16(s->ptr);
+ s->ptr += 2;
+ return 0;
+}
+
+static maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
+{
+ if (unlikely(s->buf_end - s->ptr < 4)) {
+ *pval = 0; /* avoid warning */
+ return bc_read_error_end(s);
+ }
+ *pval = get_u32(s->ptr);
+ s->ptr += 4;
+ return 0;
+}
+
+static int bc_get_u64(BCReaderState *s, uint64_t *pval)
+{
+ if (unlikely(s->buf_end - s->ptr < 8)) {
+ *pval = 0; /* avoid warning */
+ return bc_read_error_end(s);
+ }
+ *pval = get_u64(s->ptr);
+ s->ptr += 8;
+ return 0;
+}
+
+static int bc_get_leb128(BCReaderState *s, uint32_t *pval)
+{
+ int ret;
+ ret = get_leb128(pval, s->ptr, s->buf_end);
+ if (unlikely(ret < 0))
+ return bc_read_error_end(s);
+ s->ptr += ret;
+ return 0;
+}
+
+static int bc_get_sleb128(BCReaderState *s, int32_t *pval)
+{
+ int ret;
+ ret = get_sleb128(pval, s->ptr, s->buf_end);
+ if (unlikely(ret < 0))
+ return bc_read_error_end(s);
+ s->ptr += ret;
+ return 0;
+}
+
+/* XXX: used to read an `int` with a positive value */
+static int bc_get_leb128_int(BCReaderState *s, int *pval)
+{
+ return bc_get_leb128(s, (uint32_t *)pval);
+}
+
+static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
+{
+ uint32_t val;
+ if (bc_get_leb128(s, &val)) {
+ *pval = 0;
+ return -1;
+ }
+ *pval = val;
+ return 0;
+}
+
+static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)
+{
+ if (buf_len != 0) {
+ if (unlikely(!buf || s->buf_end - s->ptr < buf_len))
+ return bc_read_error_end(s);
+ memcpy(buf, s->ptr, buf_len);
+ s->ptr += buf_len;
+ }
+ return 0;
+}
+
+static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
+{
+ JSAtom atom;
+
+ if (JS_AtomIsTaggedInt(idx)) {
+ atom = idx;
+ } else if (idx < s->first_atom) {
+ atom = JS_DupAtom(s->ctx, idx);
+ } else {
+ idx -= s->first_atom;
+ if (idx >= s->idx_to_atom_count) {
+ JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",
+ (unsigned int)(s->ptr - s->buf_start));
+ *patom = JS_ATOM_NULL;
+ return s->error_state = -1;
+ }
+ atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);
+ }
+ *patom = atom;
+ return 0;
+}
+
+static int bc_get_atom(BCReaderState *s, JSAtom *patom)
+{
+ uint32_t v;
+ if (bc_get_leb128(s, &v))
+ return -1;
+ if (v & 1) {
+ *patom = JS_AtomFromUInt32(v >> 1);
+ return 0;
+ } else {
+ return bc_idx_to_atom(s, patom, v >> 1);
+ }
+}
+
+static JSString *JS_ReadString(BCReaderState *s)
+{
+ uint32_t len;
+ size_t size;
+ BOOL is_wide_char;
+ JSString *p;
+
+ if (bc_get_leb128(s, &len))
+ return NULL;
+ is_wide_char = len & 1;
+ len >>= 1;
+ p = js_alloc_string(s->ctx, len, is_wide_char);
+ if (!p) {
+ s->error_state = -1;
+ return NULL;
+ }
+ size = (size_t)len << is_wide_char;
+ if ((s->buf_end - s->ptr) < size) {
+ bc_read_error_end(s);
+ js_free_string(s->ctx->rt, p);
+ return NULL;
+ }
+ memcpy(p->u.str8, s->ptr, size);
+ s->ptr += size;
+ if (!is_wide_char) {
+ p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
+ }
+#ifdef DUMP_READ_OBJECT
+ JS_DumpString(s->ctx->rt, p); printf("\n");
+#endif
+ return p;
+}
+
+static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)
+{
+ uint32_t val;
+ /* XXX: this does not work for n == 32 */
+ val = (flags >> *pidx) & ((1U << n) - 1);
+ *pidx += n;
+ return val;
+}
+
+static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
+ int byte_code_offset, uint32_t bc_len)
+{
+ uint8_t *bc_buf;
+ int pos, len, op;
+ JSAtom atom;
+ uint32_t idx;
+
+ if (s->is_rom_data) {
+ /* directly use the input buffer */
+ if (unlikely(s->buf_end - s->ptr < bc_len))
+ return bc_read_error_end(s);
+ bc_buf = (uint8_t *)s->ptr;
+ s->ptr += bc_len;
+ } else {
+ bc_buf = (void *)((uint8_t*)b + byte_code_offset);
+ if (bc_get_buf(s, bc_buf, bc_len))
+ return -1;
+ }
+ b->byte_code_buf = bc_buf;
+
+ pos = 0;
+ while (pos < bc_len) {
+ op = bc_buf[pos];
+ len = short_opcode_info(op).size;
+ switch(short_opcode_info(op).fmt) {
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ case OP_FMT_atom_u16:
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ idx = get_u32(bc_buf + pos + 1);
+ if (s->is_rom_data) {
+ /* just increment the reference count of the atom */
+ JS_DupAtom(s->ctx, (JSAtom)idx);
+ } else {
+ if (bc_idx_to_atom(s, &atom, idx)) {
+ /* Note: the atoms will be freed up to this position */
+ b->byte_code_len = pos;
+ return -1;
+ }
+ put_u32(bc_buf + pos + 1, atom);
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+ pos += len;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_BIGNUM
+static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
+{
+ JSValue obj = JS_UNDEFINED;
+ uint8_t v8;
+ int32_t e;
+ uint32_t len;
+ limb_t l, i, n, j;
+ JSBigFloat *p;
+ limb_t v;
+ bf_t *a;
+ int bpos, d;
+
+ p = js_new_bf(s->ctx);
+ if (!p)
+ goto fail;
+ switch(tag) {
+ case BC_TAG_BIG_INT:
+ obj = JS_MKPTR(JS_TAG_BIG_INT, p);
+ break;
+ case BC_TAG_BIG_FLOAT:
+ obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
+ break;
+ case BC_TAG_BIG_DECIMAL:
+ obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
+ break;
+ default:
+ abort();
+ }
+
+ /* sign + exponent */
+ if (bc_get_sleb128(s, &e))
+ goto fail;
+
+ a = &p->num;
+ a->sign = e & 1;
+ e >>= 1;
+ if (e == 0)
+ a->expn = BF_EXP_ZERO;
+ else if (e == 1)
+ a->expn = BF_EXP_INF;
+ else if (e == 2)
+ a->expn = BF_EXP_NAN;
+ else if (e >= 3)
+ a->expn = e - 3;
+ else
+ a->expn = e;
+
+ /* mantissa */
+ if (a->expn != BF_EXP_ZERO &&
+ a->expn != BF_EXP_INF &&
+ a->expn != BF_EXP_NAN) {
+ if (bc_get_leb128(s, &len))
+ goto fail;
+ bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
+ if (len == 0) {
+ JS_ThrowInternalError(s->ctx, "invalid bignum length");
+ goto fail;
+ }
+ if (tag != BC_TAG_BIG_DECIMAL)
+ l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);
+ else
+ l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;
+ if (bf_resize(a, l)) {
+ JS_ThrowOutOfMemory(s->ctx);
+ goto fail;
+ }
+ if (tag != BC_TAG_BIG_DECIMAL) {
+ n = len & (sizeof(limb_t) - 1);
+ if (n != 0) {
+ v = 0;
+ for(i = 0; i < n; i++) {
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);
+ }
+ a->tab[0] = v;
+ i = 1;
+ } else {
+ i = 0;
+ }
+ for(; i < l; i++) {
+#if LIMB_BITS == 32
+ if (bc_get_u32(s, &v))
+ goto fail;
+#ifdef WORDS_BIGENDIAN
+ v = bswap32(v);
+#endif
+#else
+ if (bc_get_u64(s, &v))
+ goto fail;
+#ifdef WORDS_BIGENDIAN
+ v = bswap64(v);
+#endif
+#endif
+ a->tab[i] = v;
+ }
+ } else {
+ bpos = 0;
+ for(i = 0; i < l; i++) {
+ if (i == 0 && (n = len % LIMB_DIGITS) != 0) {
+ j = LIMB_DIGITS - n;
+ } else {
+ j = 0;
+ }
+ v = 0;
+ for(; j < LIMB_DIGITS; j++) {
+ if (bpos == 0) {
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ d = v8 & 0xf;
+ bpos = 1;
+ } else {
+ d = v8 >> 4;
+ bpos = 0;
+ }
+ if (d >= 10) {
+ JS_ThrowInternalError(s->ctx, "invalid digit");
+ goto fail;
+ }
+ v += mp_pow_dec[j] * d;
+ }
+ a->tab[i] = v;
+ }
+ }
+ }
+ bc_read_trace(s, "}\n");
+ return obj;
+ fail:
+ JS_FreeValue(s->ctx, obj);
+ return JS_EXCEPTION;
+}
+#endif /* CONFIG_BIGNUM */
+
+static JSValue JS_ReadObjectRec(BCReaderState *s);
+
+static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
+{
+ if (s->allow_reference) {
+ if (js_resize_array(s->ctx, (void *)&s->objects,
+ sizeof(s->objects[0]),
+ &s->objects_size, s->objects_count + 1))
+ return -1;
+ s->objects[s->objects_count++] = p;
+ }
+ return 0;
+}
+
+static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
+{
+ return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
+}
+
+static JSValue JS_ReadFunctionTag(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionBytecode bc, *b;
+ JSValue obj = JS_UNDEFINED;
+ uint16_t v16;
+ uint8_t v8;
+ int idx, i, local_count;
+ int function_size, cpool_offset, byte_code_offset;
+ int closure_var_offset, vardefs_offset;
+
+ memset(&bc, 0, sizeof(bc));
+ bc.header.ref_count = 1;
+ //bc.gc_header.mark = 0;
+
+ if (bc_get_u16(s, &v16))
+ goto fail;
+ idx = 0;
+ bc.has_prototype = bc_get_flags(v16, &idx, 1);
+ bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
+ bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
+ bc.need_home_object = bc_get_flags(v16, &idx, 1);
+ bc.func_kind = bc_get_flags(v16, &idx, 2);
+ bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
+ bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
+ bc.super_allowed = bc_get_flags(v16, &idx, 1);
+ bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
+ bc.has_debug = bc_get_flags(v16, &idx, 1);
+ bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
+ bc.read_only_bytecode = s->is_rom_data;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ bc.js_mode = v8;
+ if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.arg_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.var_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.defined_arg_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.stack_size))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.closure_var_count))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.cpool_count))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.byte_code_len))
+ goto fail;
+ if (bc_get_leb128_int(s, &local_count))
+ goto fail;
+
+ if (bc.has_debug) {
+ function_size = sizeof(*b);
+ } else {
+ function_size = offsetof(JSFunctionBytecode, debug);
+ }
+ cpool_offset = function_size;
+ function_size += bc.cpool_count * sizeof(*bc.cpool);
+ vardefs_offset = function_size;
+ function_size += local_count * sizeof(*bc.vardefs);
+ closure_var_offset = function_size;
+ function_size += bc.closure_var_count * sizeof(*bc.closure_var);
+ byte_code_offset = function_size;
+ if (!bc.read_only_bytecode) {
+ function_size += bc.byte_code_len;
+ }
+
+ b = js_mallocz(ctx, function_size);
+ if (!b)
+ return JS_EXCEPTION;
+
+ memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
+ b->header.ref_count = 1;
+ if (local_count != 0) {
+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
+ }
+ if (b->closure_var_count != 0) {
+ b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
+ }
+ if (b->cpool_count != 0) {
+ b->cpool = (void *)((uint8_t*)b + cpool_offset);
+ }
+
+ add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
+
+ obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
+
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
+#endif
+ bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
+ b->arg_count, b->var_count, b->defined_arg_count,
+ b->closure_var_count, b->cpool_count);
+ bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
+ b->stack_size, b->byte_code_len, local_count);
+
+ if (local_count != 0) {
+ bc_read_trace(s, "vars {\n");
+ for(i = 0; i < local_count; i++) {
+ JSVarDef *vd = &b->vardefs[i];
+ if (bc_get_atom(s, &vd->var_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &vd->scope_level))
+ goto fail;
+ if (bc_get_leb128_int(s, &vd->scope_next))
+ goto fail;
+ vd->scope_next--;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ idx = 0;
+ vd->var_kind = bc_get_flags(v8, &idx, 4);
+ vd->is_const = bc_get_flags(v8, &idx, 1);
+ vd->is_lexical = bc_get_flags(v8, &idx, 1);
+ vd->is_captured = bc_get_flags(v8, &idx, 1);
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
+#endif
+ }
+ bc_read_trace(s, "}\n");
+ }
+ if (b->closure_var_count != 0) {
+ bc_read_trace(s, "closure vars {\n");
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ int var_idx;
+ if (bc_get_atom(s, &cv->var_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &var_idx))
+ goto fail;
+ cv->var_idx = var_idx;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ idx = 0;
+ cv->is_local = bc_get_flags(v8, &idx, 1);
+ cv->is_arg = bc_get_flags(v8, &idx, 1);
+ cv->is_const = bc_get_flags(v8, &idx, 1);
+ cv->is_lexical = bc_get_flags(v8, &idx, 1);
+ cv->var_kind = bc_get_flags(v8, &idx, 4);
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
+#endif
+ }
+ bc_read_trace(s, "}\n");
+ }
+ {
+ bc_read_trace(s, "bytecode {\n");
+ if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
+ goto fail;
+ bc_read_trace(s, "}\n");
+ }
+ if (b->has_debug) {
+ /* read optional debug information */
+ bc_read_trace(s, "debug {\n");
+ if (bc_get_atom(s, &b->debug.filename))
+ goto fail;
+ if (bc_get_leb128_int(s, &b->debug.line_num))
+ goto fail;
+ if (bc_get_leb128_int(s, &b->debug.pc2line_len))
+ goto fail;
+ if (b->debug.pc2line_len) {
+ b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
+ if (!b->debug.pc2line_buf)
+ goto fail;
+ if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
+ goto fail;
+ }
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
+#endif
+ bc_read_trace(s, "}\n");
+ }
+ if (b->cpool_count != 0) {
+ bc_read_trace(s, "cpool {\n");
+ for(i = 0; i < b->cpool_count; i++) {
+ JSValue val;
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ b->cpool[i] = val;
+ }
+ bc_read_trace(s, "}\n");
+ }
+ b->realm = JS_DupContext(ctx);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadModule(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ JSModuleDef *m = NULL;
+ JSAtom module_name;
+ int i;
+ uint8_t v8;
+
+ if (bc_get_atom(s, &module_name))
+ goto fail;
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
+#endif
+ m = js_new_module_def(ctx, module_name);
+ if (!m)
+ goto fail;
+ obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ if (bc_get_leb128_int(s, &m->req_module_entries_count))
+ goto fail;
+ if (m->req_module_entries_count != 0) {
+ m->req_module_entries_size = m->req_module_entries_count;
+ m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
+ if (!m->req_module_entries)
+ goto fail;
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ if (bc_get_atom(s, &rme->module_name))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->export_entries_count))
+ goto fail;
+ if (m->export_entries_count != 0) {
+ m->export_entries_size = m->export_entries_count;
+ m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
+ if (!m->export_entries)
+ goto fail;
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ me->export_type = v8;
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ if (bc_get_leb128_int(s, &me->u.local.var_idx))
+ goto fail;
+ } else {
+ if (bc_get_leb128_int(s, &me->u.req_module_idx))
+ goto fail;
+ if (bc_get_atom(s, &me->local_name))
+ goto fail;
+ }
+ if (bc_get_atom(s, &me->export_name))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->star_export_entries_count))
+ goto fail;
+ if (m->star_export_entries_count != 0) {
+ m->star_export_entries_size = m->star_export_entries_count;
+ m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
+ if (!m->star_export_entries)
+ goto fail;
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ if (bc_get_leb128_int(s, &se->req_module_idx))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->import_entries_count))
+ goto fail;
+ if (m->import_entries_count != 0) {
+ m->import_entries_size = m->import_entries_count;
+ m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
+ if (!m->import_entries)
+ goto fail;
+ for(i = 0; i < m->import_entries_count; i++) {
+ JSImportEntry *mi = &m->import_entries[i];
+ if (bc_get_leb128_int(s, &mi->var_idx))
+ goto fail;
+ if (bc_get_atom(s, &mi->import_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &mi->req_module_idx))
+ goto fail;
+ }
+ }
+
+ m->func_obj = JS_ReadObjectRec(s);
+ if (JS_IsException(m->func_obj))
+ goto fail;
+ return obj;
+ fail:
+ if (m) {
+ js_free_module_def(ctx, m);
+ }
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadObjectTag(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ uint32_t prop_count, i;
+ JSAtom atom;
+ JSValue val;
+ int ret;
+
+ obj = JS_NewObject(ctx);
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ if (bc_get_leb128(s, &prop_count))
+ goto fail;
+ for(i = 0; i < prop_count; i++) {
+ if (bc_get_atom(s, &atom))
+ goto fail;
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
+#endif
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val)) {
+ JS_FreeAtom(ctx, atom);
+ goto fail;
+ }
+ ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ goto fail;
+ }
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadArray(BCReaderState *s, int tag)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ uint32_t len, i;
+ JSValue val;
+ int ret, prop_flags;
+ BOOL is_template;
+
+ obj = JS_NewArray(ctx);
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
+ if (bc_get_leb128(s, &len))
+ goto fail;
+ for(i = 0; i < len; i++) {
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (is_template)
+ prop_flags = JS_PROP_ENUMERABLE;
+ else
+ prop_flags = JS_PROP_C_W_E;
+ ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
+ prop_flags);
+ if (ret < 0)
+ goto fail;
+ }
+ if (is_template) {
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (!JS_IsUndefined(val)) {
+ ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
+ if (ret < 0)
+ goto fail;
+ }
+ JS_PreventExtensions(ctx, obj);
+ }
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadTypedArray(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
+ uint8_t array_tag;
+ JSValueConst args[3];
+ uint32_t offset, len, idx;
+
+ if (bc_get_u8(s, &array_tag))
+ return JS_EXCEPTION;
+ if (array_tag >= JS_TYPED_ARRAY_COUNT)
+ return JS_ThrowTypeError(ctx, "invalid typed array");
+ if (bc_get_leb128(s, &len))
+ return JS_EXCEPTION;
+ if (bc_get_leb128(s, &offset))
+ return JS_EXCEPTION;
+ /* XXX: this hack could be avoided if the typed array could be
+ created before the array buffer */
+ idx = s->objects_count;
+ if (BC_add_object_ref1(s, NULL))
+ goto fail;
+ array_buffer = JS_ReadObjectRec(s);
+ if (JS_IsException(array_buffer))
+ return JS_EXCEPTION;
+ if (!js_get_array_buffer(ctx, array_buffer)) {
+ JS_FreeValue(ctx, array_buffer);
+ return JS_EXCEPTION;
+ }
+ args[0] = array_buffer;
+ args[1] = JS_NewInt64(ctx, offset);
+ args[2] = JS_NewInt64(ctx, len);
+ obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
+ 3, args,
+ JS_CLASS_UINT8C_ARRAY + array_tag);
+ if (JS_IsException(obj))
+ goto fail;
+ if (s->allow_reference) {
+ s->objects[idx] = JS_VALUE_GET_OBJ(obj);
+ }
+ JS_FreeValue(ctx, array_buffer);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, array_buffer);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadArrayBuffer(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ uint32_t byte_length;
+ JSValue obj;
+
+ if (bc_get_leb128(s, &byte_length))
+ return JS_EXCEPTION;
+ if (unlikely(s->buf_end - s->ptr < byte_length)) {
+ bc_read_error_end(s);
+ return JS_EXCEPTION;
+ }
+ obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ s->ptr += byte_length;
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ uint32_t byte_length;
+ uint8_t *data_ptr;
+ JSValue obj;
+ uint64_t u64;
+
+ if (bc_get_leb128(s, &byte_length))
+ return JS_EXCEPTION;
+ if (bc_get_u64(s, &u64))
+ return JS_EXCEPTION;
+ data_ptr = (uint8_t *)(uintptr_t)u64;
+ /* the SharedArrayBuffer is cloned */
+ obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,
+ JS_CLASS_SHARED_ARRAY_BUFFER,
+ data_ptr,
+ NULL, NULL, FALSE);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadDate(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue val, obj = JS_UNDEFINED;
+
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (!JS_IsNumber(val)) {
+ JS_ThrowTypeError(ctx, "Number tag expected for date");
+ goto fail;
+ }
+ obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
+ JS_CLASS_DATE);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ JS_SetObjectData(ctx, obj, val);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadObjectValue(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue val, obj = JS_UNDEFINED;
+
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ obj = JS_ToObject(ctx, val);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ JS_FreeValue(ctx, val);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadObjectRec(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ uint8_t tag;
+ JSValue obj = JS_UNDEFINED;
+
+ if (js_check_stack_overflow(ctx->rt, 0))
+ return JS_ThrowStackOverflow(ctx);
+
+ if (bc_get_u8(s, &tag))
+ return JS_EXCEPTION;
+
+ bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
+
+ switch(tag) {
+ case BC_TAG_NULL:
+ obj = JS_NULL;
+ break;
+ case BC_TAG_UNDEFINED:
+ obj = JS_UNDEFINED;
+ break;
+ case BC_TAG_BOOL_FALSE:
+ case BC_TAG_BOOL_TRUE:
+ obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
+ break;
+ case BC_TAG_INT32:
+ {
+ int32_t val;
+ if (bc_get_sleb128(s, &val))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%d\n", val);
+ obj = JS_NewInt32(ctx, val);
+ }
+ break;
+ case BC_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ if (bc_get_u64(s, &u.u64))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%g\n", u.d);
+ obj = JS_NewFloat64Impl(ctx, u.d);
+ }
+ break;
+ case BC_TAG_STRING:
+ {
+ JSString *p;
+ p = JS_ReadString(s);
+ if (!p)
+ return JS_EXCEPTION;
+ obj = JS_MKPTR(JS_TAG_STRING, p);
+ }
+ break;
+ case BC_TAG_FUNCTION_BYTECODE:
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ obj = JS_ReadFunctionTag(s);
+ break;
+ case BC_TAG_MODULE:
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ obj = JS_ReadModule(s);
+ break;
+ case BC_TAG_OBJECT:
+ obj = JS_ReadObjectTag(s);
+ break;
+ case BC_TAG_ARRAY:
+ case BC_TAG_TEMPLATE_OBJECT:
+ obj = JS_ReadArray(s, tag);
+ break;
+ case BC_TAG_TYPED_ARRAY:
+ obj = JS_ReadTypedArray(s);
+ break;
+ case BC_TAG_ARRAY_BUFFER:
+ obj = JS_ReadArrayBuffer(s);
+ break;
+ case BC_TAG_SHARED_ARRAY_BUFFER:
+ if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
+ goto invalid_tag;
+ obj = JS_ReadSharedArrayBuffer(s);
+ break;
+ case BC_TAG_DATE:
+ obj = JS_ReadDate(s);
+ break;
+ case BC_TAG_OBJECT_VALUE:
+ obj = JS_ReadObjectValue(s);
+ break;
+#ifdef CONFIG_BIGNUM
+ case BC_TAG_BIG_INT:
+ case BC_TAG_BIG_FLOAT:
+ case BC_TAG_BIG_DECIMAL:
+ obj = JS_ReadBigNum(s, tag);
+ break;
+#endif
+ case BC_TAG_OBJECT_REFERENCE:
+ {
+ uint32_t val;
+ if (!s->allow_reference)
+ return JS_ThrowSyntaxError(ctx, "object references are not allowed");
+ if (bc_get_leb128(s, &val))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%u\n", val);
+ if (val >= s->objects_count) {
+ return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
+ val, s->objects_count);
+ }
+ obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
+ }
+ break;
+ default:
+ invalid_tag:
+ return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
+ tag, (unsigned int)(s->ptr - s->buf_start));
+ }
+ bc_read_trace(s, "}\n");
+ return obj;
+}
+
+static int JS_ReadObjectAtoms(BCReaderState *s)
+{
+ uint8_t v8;
+ JSString *p;
+ int i;
+ JSAtom atom;
+
+ if (bc_get_u8(s, &v8))
+ return -1;
+ /* XXX: could support byte swapped input */
+ if (v8 != BC_VERSION) {
+ JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
+ v8, BC_VERSION);
+ return -1;
+ }
+ if (bc_get_leb128(s, &s->idx_to_atom_count))
+ return -1;
+
+ bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);
+
+ if (s->idx_to_atom_count != 0) {
+ s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *
+ sizeof(s->idx_to_atom[0]));
+ if (!s->idx_to_atom)
+ return s->error_state = -1;
+ }
+ for(i = 0; i < s->idx_to_atom_count; i++) {
+ p = JS_ReadString(s);
+ if (!p)
+ return -1;
+ atom = JS_NewAtomStr(s->ctx, p);
+ if (atom == JS_ATOM_NULL)
+ return s->error_state = -1;
+ s->idx_to_atom[i] = atom;
+ if (s->is_rom_data && (atom != (i + s->first_atom)))
+ s->is_rom_data = FALSE; /* atoms must be relocated */
+ }
+ bc_read_trace(s, "}\n");
+ return 0;
+}
+
+static void bc_reader_free(BCReaderState *s)
+{
+ int i;
+ if (s->idx_to_atom) {
+ for(i = 0; i < s->idx_to_atom_count; i++) {
+ JS_FreeAtom(s->ctx, s->idx_to_atom[i]);
+ }
+ js_free(s->ctx, s->idx_to_atom);
+ }
+ js_free(s->ctx, s->objects);
+}
+
+JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
+ int flags)
+{
+ BCReaderState ss, *s = &ss;
+ JSValue obj;
+
+ ctx->binary_object_count += 1;
+ ctx->binary_object_size += buf_len;
+
+ memset(s, 0, sizeof(*s));
+ s->ctx = ctx;
+ s->buf_start = buf;
+ s->buf_end = buf + buf_len;
+ s->ptr = buf;
+ s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
+ s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
+ s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
+ s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
+ if (s->allow_bytecode)
+ s->first_atom = JS_ATOM_END;
+ else
+ s->first_atom = 1;
+ if (JS_ReadObjectAtoms(s)) {
+ obj = JS_EXCEPTION;
+ } else {
+ obj = JS_ReadObjectRec(s);
+ }
+ bc_reader_free(s);
+ return obj;
+}
+
+/*******************************************************************/
+/* runtime functions & objects */
+
+static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+static int check_function(JSContext *ctx, JSValueConst obj)
+{
+ if (likely(JS_IsFunction(ctx, obj)))
+ return 0;
+ JS_ThrowTypeError(ctx, "not a function");
+ return -1;
+}
+
+static int check_exception_free(JSContext *ctx, JSValue obj)
+{
+ JS_FreeValue(ctx, obj);
+ return JS_IsException(obj);
+}
+
+static JSAtom find_atom(JSContext *ctx, const char *name)
+{
+ JSAtom atom;
+ int len;
+
+ if (*name == '[') {
+ name++;
+ len = strlen(name) - 1;
+ /* We assume 8 bit non null strings, which is the case for these
+ symbols */
+ for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {
+ JSAtomStruct *p = ctx->rt->atom_array[atom];
+ JSString *str = p;
+ if (str->len == len && !memcmp(str->u.str8, name, len))
+ return JS_DupAtom(ctx, atom);
+ }
+ abort();
+ } else {
+ atom = JS_NewAtom(ctx, name);
+ }
+ return atom;
+}
+
+static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
+ JSAtom atom, void *opaque)
+{
+ const JSCFunctionListEntry *e = opaque;
+ JSValue val;
+
+ switch(e->def_type) {
+ case JS_DEF_CFUNC:
+ val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
+ e->name, e->u.func.length, e->u.func.cproto, e->magic);
+ break;
+ case JS_DEF_PROP_STRING:
+ val = JS_NewAtomString(ctx, e->u.str);
+ break;
+ case JS_DEF_OBJECT:
+ val = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
+ break;
+ default:
+ abort();
+ }
+ return val;
+}
+
+static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
+ JSAtom atom,
+ const JSCFunctionListEntry *e)
+{
+ JSValue val;
+ int prop_flags = e->prop_flags;
+
+ switch(e->def_type) {
+ case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */
+ {
+ JSAtom atom1 = find_atom(ctx, e->u.alias.name);
+ switch (e->u.alias.base) {
+ case -1:
+ val = JS_GetProperty(ctx, obj, atom1);
+ break;
+ case 0:
+ val = JS_GetProperty(ctx, ctx->global_obj, atom1);
+ break;
+ case 1:
+ val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeAtom(ctx, atom1);
+ if (atom == JS_ATOM_Symbol_toPrimitive) {
+ /* Symbol.toPrimitive functions are not writable */
+ prop_flags = JS_PROP_CONFIGURABLE;
+ } else if (atom == JS_ATOM_Symbol_hasInstance) {
+ /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
+ prop_flags = 0;
+ }
+ }
+ break;
+ case JS_DEF_CFUNC:
+ if (atom == JS_ATOM_Symbol_toPrimitive) {
+ /* Symbol.toPrimitive functions are not writable */
+ prop_flags = JS_PROP_CONFIGURABLE;
+ } else if (atom == JS_ATOM_Symbol_hasInstance) {
+ /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
+ prop_flags = 0;
+ }
+ JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
+ (void *)e, prop_flags);
+ return 0;
+ case JS_DEF_CGETSET: /* XXX: use autoinit again ? */
+ case JS_DEF_CGETSET_MAGIC:
+ {
+ JSValue getter, setter;
+ char buf[64];
+
+ getter = JS_UNDEFINED;
+ if (e->u.getset.get.generic) {
+ snprintf(buf, sizeof(buf), "get %s", e->name);
+ getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,
+ buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,
+ e->magic);
+ }
+ setter = JS_UNDEFINED;
+ if (e->u.getset.set.generic) {
+ snprintf(buf, sizeof(buf), "set %s", e->name);
+ setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,
+ buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,
+ e->magic);
+ }
+ JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);
+ return 0;
+ }
+ break;
+ case JS_DEF_PROP_INT32:
+ val = JS_NewInt32(ctx, e->u.i32);
+ break;
+ case JS_DEF_PROP_INT64:
+ val = JS_NewInt64(ctx, e->u.i64);
+ break;
+ case JS_DEF_PROP_DOUBLE:
+ val = JS_NewFloat64Impl(ctx, e->u.f64);
+ break;
+ case JS_DEF_PROP_UNDEFINED:
+ val = JS_UNDEFINED;
+ break;
+ case JS_DEF_PROP_STRING:
+ case JS_DEF_OBJECT:
+ JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
+ (void *)e, prop_flags);
+ return 0;
+ default:
+ abort();
+ }
+ JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);
+ return 0;
+}
+
+void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
+ const JSCFunctionListEntry *tab, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ const JSCFunctionListEntry *e = &tab[i];
+ JSAtom atom = find_atom(ctx, e->name);
+ JS_InstantiateFunctionListItem(ctx, obj, atom, e);
+ JS_FreeAtom(ctx, atom);
+ }
+}
+
+int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
+ const JSCFunctionListEntry *tab, int len)
+{
+ int i;
+ for(i = 0; i < len; i++) {
+ if (JS_AddModuleExport(ctx, m, tab[i].name))
+ return -1;
+ }
+ return 0;
+}
+
+int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
+ const JSCFunctionListEntry *tab, int len)
+{
+ int i;
+ JSValue val;
+
+ for(i = 0; i < len; i++) {
+ const JSCFunctionListEntry *e = &tab[i];
+ switch(e->def_type) {
+ case JS_DEF_CFUNC:
+ val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
+ e->name, e->u.func.length, e->u.func.cproto, e->magic);
+ break;
+ case JS_DEF_PROP_STRING:
+ val = JS_NewString(ctx, e->u.str);
+ break;
+ case JS_DEF_PROP_INT32:
+ val = JS_NewInt32(ctx, e->u.i32);
+ break;
+ case JS_DEF_PROP_INT64:
+ val = JS_NewInt64(ctx, e->u.i64);
+ break;
+ case JS_DEF_PROP_DOUBLE:
+ val = JS_NewFloat64Impl(ctx, e->u.f64);
+ break;
+ case JS_DEF_OBJECT:
+ val = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
+ break;
+ default:
+ abort();
+ }
+ if (JS_SetModuleExport(ctx, m, e->name, val))
+ return -1;
+ }
+ return 0;
+}
+
+/* Note: 'func_obj' is not necessarily a constructor */
+static void JS_SetConstructor2(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst proto,
+ int proto_flags, int ctor_flags)
+{
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,
+ JS_DupValue(ctx, proto), proto_flags);
+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
+ JS_DupValue(ctx, func_obj),
+ ctor_flags);
+ set_cycle_flag(ctx, func_obj);
+ set_cycle_flag(ctx, proto);
+}
+
+void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst proto)
+{
+ JS_SetConstructor2(ctx, func_obj, proto,
+ 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+}
+
+static void JS_NewGlobalCConstructor2(JSContext *ctx,
+ JSValue func_obj,
+ const char *name,
+ JSValueConst proto)
+{
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,
+ JS_DupValue(ctx, func_obj),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_SetConstructor(ctx, func_obj, proto);
+ JS_FreeValue(ctx, func_obj);
+}
+
+static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,
+ JSCFunction *func, int length,
+ JSValueConst proto)
+{
+ JSValue func_obj;
+ func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);
+ JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
+ return func_obj;
+}
+
+static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,
+ JSCFunction *func, int length,
+ JSValueConst proto)
+{
+ JSValue func_obj;
+ func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);
+ JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
+ return func_obj;
+}
+
+static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);
+}
+
+static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double d;
+
+ /* XXX: does this work for bigfloat? */
+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
+ return JS_EXCEPTION;
+ return JS_NewBool(ctx, isnan(d));
+}
+
+static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ BOOL res;
+ double d;
+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
+ return JS_EXCEPTION;
+ res = isfinite(d);
+ return JS_NewBool(ctx, res);
+}
+
+/* Object class */
+
+static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
+{
+ int tag = JS_VALUE_GET_NORM_TAG(val);
+ JSValue obj;
+
+ switch(tag) {
+ default:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ return JS_ThrowTypeError(ctx, "cannot convert to object");
+ case JS_TAG_OBJECT:
+ case JS_TAG_EXCEPTION:
+ return JS_DupValue(ctx, val);
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
+ goto set_value;
+ case JS_TAG_BIG_FLOAT:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
+ goto set_value;
+ case JS_TAG_BIG_DECIMAL:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
+ goto set_value;
+#endif
+ case JS_TAG_INT:
+ case JS_TAG_FLOAT64:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
+ goto set_value;
+ case JS_TAG_STRING:
+ /* XXX: should call the string constructor */
+ {
+ JSString *p1 = JS_VALUE_GET_STRING(val);
+ obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
+ }
+ goto set_value;
+ case JS_TAG_BOOL:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);
+ goto set_value;
+ case JS_TAG_SYMBOL:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);
+ set_value:
+ if (!JS_IsException(obj))
+ JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));
+ return obj;
+ }
+}
+
+static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)
+{
+ JSValue obj = JS_ToObject(ctx, val);
+ JS_FreeValue(ctx, val);
+ return obj;
+}
+
+static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
+ JSValueConst desc)
+{
+ JSValue val, getter, setter;
+ int flags;
+
+ if (!JS_IsObject(desc)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ flags = 0;
+ val = JS_UNDEFINED;
+ getter = JS_UNDEFINED;
+ setter = JS_UNDEFINED;
+ if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {
+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
+ if (JS_IsException(prop))
+ goto fail;
+ flags |= JS_PROP_HAS_CONFIGURABLE;
+ if (JS_ToBoolFree(ctx, prop))
+ flags |= JS_PROP_CONFIGURABLE;
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {
+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
+ if (JS_IsException(prop))
+ goto fail;
+ flags |= JS_PROP_HAS_WRITABLE;
+ if (JS_ToBoolFree(ctx, prop))
+ flags |= JS_PROP_WRITABLE;
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {
+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
+ if (JS_IsException(prop))
+ goto fail;
+ flags |= JS_PROP_HAS_ENUMERABLE;
+ if (JS_ToBoolFree(ctx, prop))
+ flags |= JS_PROP_ENUMERABLE;
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {
+ flags |= JS_PROP_HAS_VALUE;
+ val = JS_GetProperty(ctx, desc, JS_ATOM_value);
+ if (JS_IsException(val))
+ goto fail;
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {
+ flags |= JS_PROP_HAS_GET;
+ getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
+ if (JS_IsException(getter) ||
+ !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {
+ JS_ThrowTypeError(ctx, "invalid getter");
+ goto fail;
+ }
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {
+ flags |= JS_PROP_HAS_SET;
+ setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
+ if (JS_IsException(setter) ||
+ !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {
+ JS_ThrowTypeError(ctx, "invalid setter");
+ goto fail;
+ }
+ }
+ if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&
+ (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {
+ JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");
+ goto fail;
+ }
+ d->flags = flags;
+ d->value = val;
+ d->getter = getter;
+ d->setter = setter;
+ return 0;
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, getter);
+ JS_FreeValue(ctx, setter);
+ return -1;
+}
+
+static warn_unused int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst desc,
+ int flags)
+{
+ JSPropertyDescriptor d;
+ int ret;
+
+ if (js_obj_to_desc(ctx, &d, desc) < 0)
+ return -1;
+
+ ret = JS_DefineProperty(ctx, obj, prop,
+ d.value, d.getter, d.setter, d.flags | flags);
+ js_free_desc(ctx, &d);
+ return ret;
+}
+
+static warn_unused int JS_ObjectDefineProperties(JSContext *ctx,
+ JSValueConst obj,
+ JSValueConst properties)
+{
+ JSValue props, desc;
+ JSObject *p;
+ JSPropertyEnum *atoms;
+ uint32_t len, i;
+ int ret = -1;
+
+ if (!JS_IsObject(obj)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ desc = JS_UNDEFINED;
+ props = JS_ToObject(ctx, properties);
+ if (JS_IsException(props))
+ return -1;
+ p = JS_VALUE_GET_OBJ(props);
+ if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
+ goto exception;
+ for(i = 0; i < len; i++) {
+ JS_FreeValue(ctx, desc);
+ desc = JS_GetProperty(ctx, props, atoms[i].atom);
+ if (JS_IsException(desc))
+ goto exception;
+ if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ ret = 0;
+
+exception:
+ js_free_prop_enum(ctx, atoms, len);
+ JS_FreeValue(ctx, props);
+ JS_FreeValue(ctx, desc);
+ return ret;
+}
+
+static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue ret;
+ if (!JS_IsUndefined(new_target) &&
+ JS_VALUE_GET_OBJ(new_target) !=
+ JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {
+ ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
+ } else {
+ int tag = JS_VALUE_GET_NORM_TAG(argv[0]);
+ switch(tag) {
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_NewObject(ctx);
+ break;
+ default:
+ ret = JS_ToObject(ctx, argv[0]);
+ break;
+ }
+ }
+ return ret;
+}
+
+static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst proto, props;
+ JSValue obj;
+
+ proto = argv[0];
+ if (!JS_IsObject(proto) && !JS_IsNull(proto))
+ return JS_ThrowTypeError(ctx, "not a prototype");
+ obj = JS_NewObjectProto(ctx, proto);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ props = argv[1];
+ if (!JS_IsUndefined(props)) {
+ if (JS_ObjectDefineProperties(ctx, obj, props)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ }
+ return obj;
+}
+
+static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValueConst val;
+
+ val = argv[0];
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {
+ /* ES6 feature non compatible with ES5.1: primitive types are
+ accepted */
+ if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||
+ JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ }
+ return JS_GetPrototype(ctx, val);
+}
+
+static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj;
+ obj = argv[0];
+ if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)
+ return JS_EXCEPTION;
+ return JS_DupValue(ctx, obj);
+}
+
+/* magic = 1 if called as Reflect.defineProperty */
+static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValueConst obj, prop, desc;
+ int ret, flags;
+ JSAtom atom;
+
+ obj = argv[0];
+ prop = argv[1];
+ desc = argv[2];
+
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ flags = 0;
+ if (!magic)
+ flags |= JS_PROP_THROW;
+ ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0) {
+ return JS_EXCEPTION;
+ } else if (magic) {
+ return JS_NewBool(ctx, ret);
+ } else {
+ return JS_DupValue(ctx, obj);
+ }
+}
+
+static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // defineProperties(obj, properties)
+ JSValueConst obj = argv[0];
+
+ if (JS_ObjectDefineProperties(ctx, obj, argv[1]))
+ return JS_EXCEPTION;
+ else
+ return JS_DupValue(ctx, obj);
+}
+
+/* magic = 1 if called as __defineSetter__ */
+static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue obj;
+ JSValueConst prop, value, get, set;
+ int ret, flags;
+ JSAtom atom;
+
+ prop = argv[0];
+ value = argv[1];
+
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+
+ if (check_function(ctx, value)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ flags = JS_PROP_THROW |
+ JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;
+ if (magic) {
+ get = JS_UNDEFINED;
+ set = value;
+ flags |= JS_PROP_HAS_SET;
+ } else {
+ get = value;
+ set = JS_UNDEFINED;
+ flags |= JS_PROP_HAS_GET;
+ }
+ ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);
+ JS_FreeValue(ctx, obj);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0) {
+ return JS_EXCEPTION;
+ } else {
+ return JS_UNDEFINED;
+ }
+}
+
+static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValueConst prop;
+ JSAtom atom;
+ JSValue ret, obj;
+ JSPropertyDescriptor desc;
+ int res, flags;
+
+ if (magic) {
+ /* Reflect.getOwnPropertyDescriptor case */
+ if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ obj = JS_DupValue(ctx, argv[0]);
+ } else {
+ obj = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(obj))
+ return obj;
+ }
+ prop = argv[1];
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ goto exception;
+ ret = JS_UNDEFINED;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ ret = JS_NewObject(ctx);
+ if (JS_IsException(ret))
+ goto exception1;
+ flags = JS_PROP_C_W_E | JS_PROP_THROW;
+ if (desc.flags & JS_PROP_GETSET) {
+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0
+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)
+ goto exception1;
+ } else {
+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0
+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
+ JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0)
+ goto exception1;
+ }
+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
+ JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0
+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
+ JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0)
+ goto exception1;
+ js_free_desc(ctx, &desc);
+ }
+ }
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, obj);
+ return ret;
+
+exception1:
+ js_free_desc(ctx, &desc);
+ JS_FreeValue(ctx, ret);
+exception:
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ //getOwnPropertyDescriptors(obj)
+ JSValue obj, r;
+ JSObject *p;
+ JSPropertyEnum *props;
+ uint32_t len, i;
+
+ r = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
+ goto exception;
+ r = JS_NewObject(ctx);
+ if (JS_IsException(r))
+ goto exception;
+ for(i = 0; i < len; i++) {
+ JSValue atomValue, desc;
+ JSValueConst args[2];
+
+ atomValue = JS_AtomToValue(ctx, props[i].atom);
+ if (JS_IsException(atomValue))
+ goto exception;
+ args[0] = obj;
+ args[1] = atomValue;
+ desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);
+ JS_FreeValue(ctx, atomValue);
+ if (JS_IsException(desc))
+ goto exception;
+ if (!JS_IsUndefined(desc)) {
+ if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ js_free_prop_enum(ctx, props, len);
+ JS_FreeValue(ctx, obj);
+ return r;
+
+exception:
+ js_free_prop_enum(ctx, props, len);
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, r);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,
+ int flags, int kind)
+{
+ JSValue obj, r, val, key, value;
+ JSObject *p;
+ JSPropertyEnum *atoms;
+ uint32_t len, i, j;
+
+ r = JS_UNDEFINED;
+ val = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, obj1);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))
+ goto exception;
+ r = JS_NewArray(ctx);
+ if (JS_IsException(r))
+ goto exception;
+ for(j = i = 0; i < len; i++) {
+ JSAtom atom = atoms[i].atom;
+ if (flags & JS_GPN_ENUM_ONLY) {
+ JSPropertyDescriptor desc;
+ int res;
+
+ /* Check if property is still enumerable */
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
+ if (res < 0)
+ goto exception;
+ if (!res)
+ continue;
+ js_free_desc(ctx, &desc);
+ if (!(desc.flags & JS_PROP_ENUMERABLE))
+ continue;
+ }
+ switch(kind) {
+ default:
+ case JS_ITERATOR_KIND_KEY:
+ val = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(val))
+ goto exception;
+ break;
+ case JS_ITERATOR_KIND_VALUE:
+ val = JS_GetProperty(ctx, obj, atom);
+ if (JS_IsException(val))
+ goto exception;
+ break;
+ case JS_ITERATOR_KIND_KEY_AND_VALUE:
+ val = JS_NewArray(ctx);
+ if (JS_IsException(val))
+ goto exception;
+ key = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(key))
+ goto exception1;
+ if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)
+ goto exception1;
+ value = JS_GetProperty(ctx, obj, atom);
+ if (JS_IsException(value))
+ goto exception1;
+ if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)
+ goto exception1;
+ break;
+ }
+ if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)
+ goto exception;
+ }
+ goto done;
+
+exception1:
+ JS_FreeValue(ctx, val);
+exception:
+ JS_FreeValue(ctx, r);
+ r = JS_EXCEPTION;
+done:
+ js_free_prop_enum(ctx, atoms, len);
+ JS_FreeValue(ctx, obj);
+ return r;
+}
+
+static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_GetOwnPropertyNames2(ctx, argv[0],
+ JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);
+}
+
+static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_GetOwnPropertyNames2(ctx, argv[0],
+ JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);
+}
+
+static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int kind)
+{
+ return JS_GetOwnPropertyNames2(ctx, argv[0],
+ JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);
+}
+
+static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int reflect)
+{
+ JSValueConst obj;
+ int ret;
+
+ obj = argv[0];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
+ if (reflect)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ else
+ return JS_FALSE;
+ }
+ ret = JS_IsExtensible(ctx, obj);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int reflect)
+{
+ JSValueConst obj;
+ int ret;
+
+ obj = argv[0];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
+ if (reflect)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ else
+ return JS_DupValue(ctx, obj);
+ }
+ ret = JS_PreventExtensions(ctx, obj);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ if (reflect) {
+ return JS_NewBool(ctx, ret);
+ } else {
+ if (!ret)
+ return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
+ return JS_DupValue(ctx, obj);
+ }
+}
+
+static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ JSAtom atom;
+ JSObject *p;
+ BOOL ret;
+
+ atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj)) {
+ JS_FreeAtom(ctx, atom);
+ return obj;
+ }
+ p = JS_VALUE_GET_OBJ(obj);
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, obj);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToObject(ctx, this_val);
+}
+
+static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, tag;
+ int is_array;
+ JSAtom atom;
+ JSObject *p;
+
+ if (JS_IsNull(this_val)) {
+ tag = JS_NewString(ctx, "Null");
+ } else if (JS_IsUndefined(this_val)) {
+ tag = JS_NewString(ctx, "Undefined");
+ } else {
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ return obj;
+ is_array = JS_IsArray(ctx, obj);
+ if (is_array < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ if (is_array) {
+ atom = JS_ATOM_Array;
+ } else if (JS_IsFunction(ctx, obj)) {
+ atom = JS_ATOM_Function;
+ } else {
+ p = JS_VALUE_GET_OBJ(obj);
+ switch(p->class_id) {
+ case JS_CLASS_STRING:
+ case JS_CLASS_ARGUMENTS:
+ case JS_CLASS_MAPPED_ARGUMENTS:
+ case JS_CLASS_ERROR:
+ case JS_CLASS_BOOLEAN:
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_DATE:
+ case JS_CLASS_REGEXP:
+ atom = ctx->rt->class_array[p->class_id].class_name;
+ break;
+ default:
+ atom = JS_ATOM_Object;
+ break;
+ }
+ }
+ tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);
+ JS_FreeValue(ctx, obj);
+ if (JS_IsException(tag))
+ return JS_EXCEPTION;
+ if (!JS_IsString(tag)) {
+ JS_FreeValue(ctx, tag);
+ tag = JS_AtomToString(ctx, atom);
+ }
+ }
+ return JS_ConcatString3(ctx, "[object ", tag, "]");
+}
+
+static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);
+}
+
+static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // Object.assign(obj, source1)
+ JSValue obj, s;
+ int i;
+
+ s = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(obj))
+ goto exception;
+ for (i = 1; i < argc; i++) {
+ if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {
+ s = JS_ToObject(ctx, argv[i]);
+ if (JS_IsException(s))
+ goto exception;
+ if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))
+ goto exception;
+ JS_FreeValue(ctx, s);
+ }
+ }
+ return obj;
+exception:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, s);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int freeze_flag)
+{
+ JSValueConst obj = argv[0];
+ JSObject *p;
+ JSPropertyEnum *props;
+ uint32_t len, i;
+ int flags, desc_flags, res;
+
+ if (!JS_IsObject(obj))
+ return JS_DupValue(ctx, obj);
+
+ res = JS_PreventExtensions(ctx, obj);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res) {
+ return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
+ }
+
+ p = JS_VALUE_GET_OBJ(obj);
+ flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
+ return JS_EXCEPTION;
+
+ for(i = 0; i < len; i++) {
+ JSPropertyDescriptor desc;
+ JSAtom prop = props[i].atom;
+
+ desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;
+ if (freeze_flag) {
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ if (desc.flags & JS_PROP_WRITABLE)
+ desc_flags |= JS_PROP_HAS_WRITABLE;
+ js_free_desc(ctx, &desc);
+ }
+ }
+ if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,
+ JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)
+ goto exception;
+ }
+ js_free_prop_enum(ctx, props, len);
+ return JS_DupValue(ctx, obj);
+
+ exception:
+ js_free_prop_enum(ctx, props, len);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int is_frozen)
+{
+ JSValueConst obj = argv[0];
+ JSObject *p;
+ JSPropertyEnum *props;
+ uint32_t len, i;
+ int flags, res;
+
+ if (!JS_IsObject(obj))
+ return JS_TRUE;
+
+ p = JS_VALUE_GET_OBJ(obj);
+ flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
+ return JS_EXCEPTION;
+
+ for(i = 0; i < len; i++) {
+ JSPropertyDescriptor desc;
+ JSAtom prop = props[i].atom;
+
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ js_free_desc(ctx, &desc);
+ if ((desc.flags & JS_PROP_CONFIGURABLE)
+ || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {
+ res = FALSE;
+ goto done;
+ }
+ }
+ }
+ res = JS_IsExtensible(ctx, obj);
+ if (res < 0)
+ return JS_EXCEPTION;
+ res ^= 1;
+done:
+ js_free_prop_enum(ctx, props, len);
+ return JS_NewBool(ctx, res);
+
+exception:
+ js_free_prop_enum(ctx, props, len);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, iter, next_method = JS_UNDEFINED;
+ JSValueConst iterable;
+ BOOL done;
+
+ /* RequireObjectCoercible() not necessary because it is tested in
+ JS_GetIterator() by JS_GetProperty() */
+ iterable = argv[0];
+
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ return obj;
+
+ iter = JS_GetIterator(ctx, iterable, FALSE);
+ if (JS_IsException(iter))
+ goto fail;
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail;
+
+ for(;;) {
+ JSValue key, value, item;
+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(item))
+ goto fail;
+ if (done) {
+ JS_FreeValue(ctx, item);
+ break;
+ }
+
+ key = JS_UNDEFINED;
+ value = JS_UNDEFINED;
+ if (!JS_IsObject(item)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto fail1;
+ }
+ key = JS_GetPropertyUint32(ctx, item, 0);
+ if (JS_IsException(key))
+ goto fail1;
+ value = JS_GetPropertyUint32(ctx, item, 1);
+ if (JS_IsException(value)) {
+ JS_FreeValue(ctx, key);
+ goto fail1;
+ }
+ if (JS_DefinePropertyValueValue(ctx, obj, key, value,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0) {
+ fail1:
+ JS_FreeValue(ctx, item);
+ goto fail;
+ }
+ JS_FreeValue(ctx, item);
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ return obj;
+ fail:
+ if (JS_IsObject(iter)) {
+ /* close the iterator object, preserving pending exception */
+ JS_IteratorClose(ctx, iter, TRUE);
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+#if 0
+/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */
+static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),
+ JS_DupValue(ctx, argv[2]),
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToObject(ctx, argv[0]);
+}
+
+static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int hint = HINT_NONE;
+
+ if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)
+ hint = JS_VALUE_GET_INT(argv[1]);
+
+ return JS_ToPrimitive(ctx, argv[0], hint);
+}
+#endif
+
+/* return an empty string if not an object */
+static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSAtom atom;
+ JSObject *p;
+ uint32_t tag;
+ int class_id;
+
+ tag = JS_VALUE_GET_NORM_TAG(argv[0]);
+ if (tag == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(argv[0]);
+ class_id = p->class_id;
+ if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))
+ class_id = JS_CLASS_BYTECODE_FUNCTION;
+ atom = ctx->rt->class_array[class_id].class_name;
+ } else {
+ atom = JS_ATOM_empty_string;
+ }
+ return JS_AtomToString(ctx, atom);
+}
+
+static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));
+}
+
+#if 0
+static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_GetObjectData(ctx, argv[0]);
+}
+
+static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))
+ return JS_EXCEPTION;
+ return JS_DupValue(ctx, argv[1]);
+}
+
+static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToPropertyKey(ctx, argv[0]);
+}
+
+static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewBool(ctx, JS_IsObject(argv[0]));
+}
+
+static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));
+}
+
+static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));
+}
+#endif
+
+static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,
+ JSValueConst defaultConstructor)
+{
+ JSValue ctor, species;
+
+ if (!JS_IsObject(obj))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
+ if (JS_IsException(ctor))
+ return ctor;
+ if (JS_IsUndefined(ctor))
+ return JS_DupValue(ctx, defaultConstructor);
+ if (!JS_IsObject(ctor)) {
+ JS_FreeValue(ctx, ctor);
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ }
+ species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
+ JS_FreeValue(ctx, ctor);
+ if (JS_IsException(species))
+ return species;
+ if (JS_IsUndefined(species) || JS_IsNull(species))
+ return JS_DupValue(ctx, defaultConstructor);
+ if (!JS_IsConstructor(ctx, species)) {
+ JS_FreeValue(ctx, species);
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ }
+ return species;
+}
+
+#if 0
+static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_SpeciesConstructor(ctx, argv[0], argv[1]);
+}
+#endif
+
+static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
+{
+ JSValue val, ret;
+
+ val = JS_ToObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ ret = JS_GetPrototype(ctx, val);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
+ JSValueConst proto)
+{
+ if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ if (!JS_IsObject(proto) && !JS_IsNull(proto))
+ return JS_UNDEFINED;
+ if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_UNDEFINED;
+}
+
+static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, v1;
+ JSValueConst v;
+ int res;
+
+ v = argv[0];
+ if (!JS_IsObject(v))
+ return JS_FALSE;
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ v1 = JS_DupValue(ctx, v);
+ for(;;) {
+ v1 = JS_GetPrototypeFree(ctx, v1);
+ if (JS_IsException(v1))
+ goto exception;
+ if (JS_IsNull(v1)) {
+ res = FALSE;
+ break;
+ }
+ if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
+ res = TRUE;
+ break;
+ }
+ /* avoid infinite loop (possible with proxies) */
+ if (js_poll_interrupts(ctx))
+ goto exception;
+ }
+ JS_FreeValue(ctx, v1);
+ JS_FreeValue(ctx, obj);
+ return JS_NewBool(ctx, res);
+
+exception:
+ JS_FreeValue(ctx, v1);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, res = JS_EXCEPTION;
+ JSAtom prop = JS_ATOM_NULL;
+ JSPropertyDescriptor desc;
+ int has_prop;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ goto exception;
+ prop = JS_ValueToAtom(ctx, argv[0]);
+ if (unlikely(prop == JS_ATOM_NULL))
+ goto exception;
+
+ has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
+ if (has_prop < 0)
+ goto exception;
+ if (has_prop) {
+ res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0);
+ js_free_desc(ctx, &desc);
+ } else {
+ res = JS_FALSE;
+ }
+
+exception:
+ JS_FreeAtom(ctx, prop);
+ JS_FreeValue(ctx, obj);
+ return res;
+}
+
+static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int setter)
+{
+ JSValue obj, res = JS_EXCEPTION;
+ JSAtom prop = JS_ATOM_NULL;
+ JSPropertyDescriptor desc;
+ int has_prop;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ goto exception;
+ prop = JS_ValueToAtom(ctx, argv[0]);
+ if (unlikely(prop == JS_ATOM_NULL))
+ goto exception;
+
+ for (;;) {
+ has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
+ if (has_prop < 0)
+ goto exception;
+ if (has_prop) {
+ if (desc.flags & JS_PROP_GETSET)
+ res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);
+ else
+ res = JS_UNDEFINED;
+ js_free_desc(ctx, &desc);
+ break;
+ }
+ obj = JS_GetPrototypeFree(ctx, obj);
+ if (JS_IsException(obj))
+ goto exception;
+ if (JS_IsNull(obj)) {
+ res = JS_UNDEFINED;
+ break;
+ }
+ /* avoid infinite loop (possible with proxies) */
+ if (js_poll_interrupts(ctx))
+ goto exception;
+ }
+
+exception:
+ JS_FreeAtom(ctx, prop);
+ JS_FreeValue(ctx, obj);
+ return res;
+}
+
+static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ JSAtom atom;
+ JSObject *p;
+ BOOL ret;
+
+ obj = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(obj))
+ return obj;
+ atom = JS_ValueToAtom(ctx, argv[1]);
+ if (unlikely(atom == JS_ATOM_NULL)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ p = JS_VALUE_GET_OBJ(obj);
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, obj);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static const JSCFunctionListEntry js_object_funcs[] = {
+ JS_CFUNC_DEF("create", 2, js_object_create ),
+ JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
+ JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),
+ JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),
+ JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
+ JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
+ JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
+ JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
+ JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
+ JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
+ JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),
+ JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),
+ JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),
+ JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),
+ JS_CFUNC_DEF("is", 2, js_object_is ),
+ JS_CFUNC_DEF("assign", 2, js_object_assign ),
+ JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),
+ JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),
+ JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),
+ JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),
+ JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),
+ //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),
+ //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),
+ //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),
+ //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),
+ //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),
+ //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),
+ //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),
+ //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),
+ //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),
+ //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),
+ JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),
+ JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ),
+};
+
+static const JSCFunctionListEntry js_object_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_object_toString ),
+ JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),
+ JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),
+ JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),
+ JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),
+ JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),
+ JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
+ JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),
+ JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),
+ JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),
+ JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),
+};
+
+/* Function class */
+
+static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_UNDEFINED;
+}
+
+/* XXX: add a specific eval mode so that Function("}), ({") is rejected */
+static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSFunctionKindEnum func_kind = magic;
+ int i, n, ret;
+ JSValue s, proto, obj = JS_UNDEFINED;
+ StringBuffer b_s, *b = &b_s;
+
+ string_buffer_init(ctx, b, 0);
+ string_buffer_putc8(b, '(');
+
+ if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ string_buffer_puts8(b, "async ");
+ }
+ string_buffer_puts8(b, "function");
+
+ if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ string_buffer_putc8(b, '*');
+ }
+ string_buffer_puts8(b, " anonymous(");
+
+ n = argc - 1;
+ for(i = 0; i < n; i++) {
+ if (i != 0) {
+ string_buffer_putc8(b, ',');
+ }
+ if (string_buffer_concat_value(b, argv[i]))
+ goto fail;
+ }
+ string_buffer_puts8(b, "\n) {\n");
+ if (n >= 0) {
+ if (string_buffer_concat_value(b, argv[n]))
+ goto fail;
+ }
+ string_buffer_puts8(b, "\n})");
+ s = string_buffer_end(b);
+ if (JS_IsException(s))
+ goto fail1;
+
+ obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);
+ JS_FreeValue(ctx, s);
+ if (JS_IsException(obj))
+ goto fail1;
+ if (!JS_IsUndefined(new_target)) {
+ /* set the prototype */
+ proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
+ if (JS_IsException(proto))
+ goto fail1;
+ if (!JS_IsObject(proto)) {
+ JSContext *realm;
+ JS_FreeValue(ctx, proto);
+ realm = JS_GetFunctionRealm(ctx, new_target);
+ if (!realm)
+ goto fail1;
+ proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);
+ }
+ ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);
+ JS_FreeValue(ctx, proto);
+ if (ret < 0)
+ goto fail1;
+ }
+ return obj;
+
+ fail:
+ string_buffer_free(b);
+ fail1:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres,
+ JSValueConst obj)
+{
+ JSValue len_val;
+ len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
+ if (JS_IsException(len_val)) {
+ *pres = 0;
+ return -1;
+ }
+ return JS_ToUint32Free(ctx, pres, len_val);
+}
+
+static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres,
+ JSValueConst obj)
+{
+ JSValue len_val;
+ len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
+ if (JS_IsException(len_val)) {
+ *pres = 0;
+ return -1;
+ }
+ return JS_ToLengthFree(ctx, pres, len_val);
+}
+
+static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)
+{
+ uint32_t i;
+ for(i = 0; i < len; i++) {
+ JS_FreeValue(ctx, tab[i]);
+ }
+ js_free(ctx, tab);
+}
+
+/* XXX: should use ValueArray */
+static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
+ JSValueConst array_arg)
+{
+ uint32_t len, i;
+ JSValue *tab, ret;
+ JSObject *p;
+
+ if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {
+ JS_ThrowTypeError(ctx, "not a object");
+ return NULL;
+ }
+ if (js_get_length32(ctx, &len, array_arg))
+ return NULL;
+ if (len > JS_MAX_LOCAL_VARS) {
+ JS_ThrowInternalError(ctx, "too many arguments");
+ return NULL;
+ }
+ /* avoid allocating 0 bytes */
+ tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));
+ if (!tab)
+ return NULL;
+ p = JS_VALUE_GET_OBJ(array_arg);
+ if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&
+ p->fast_array &&
+ len == p->u.array.count) {
+ for(i = 0; i < len; i++) {
+ tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);
+ }
+ } else {
+ for(i = 0; i < len; i++) {
+ ret = JS_GetPropertyUint32(ctx, array_arg, i);
+ if (JS_IsException(ret)) {
+ free_arg_list(ctx, tab, i);
+ return NULL;
+ }
+ tab[i] = ret;
+ }
+ }
+ *plen = len;
+ return tab;
+}
+
+/* magic value: 0 = normal apply, 1 = apply for constructor, 2 =
+ Reflect.apply */
+static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValueConst this_arg, array_arg;
+ uint32_t len;
+ JSValue *tab, ret;
+
+ if (check_function(ctx, this_val))
+ return JS_EXCEPTION;
+ this_arg = argv[0];
+ array_arg = argv[1];
+ if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||
+ JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) {
+ return JS_Call(ctx, this_val, this_arg, 0, NULL);
+ }
+ tab = build_arg_list(ctx, &len, array_arg);
+ if (!tab)
+ return JS_EXCEPTION;
+ if (magic & 1) {
+ ret = JS_CallConstructor2(ctx, this_val, this_arg, len, tab);
+ } else {
+ ret = JS_Call(ctx, this_val, this_arg, len, tab);
+ }
+ free_arg_list(ctx, tab, len);
+ return ret;
+}
+
+static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (argc <= 0) {
+ return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);
+ } else {
+ return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);
+ }
+}
+
+static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSBoundFunction *bf;
+ JSValue func_obj, name1, len_val;
+ JSObject *p;
+ int arg_count, i, ret;
+
+ if (check_function(ctx, this_val))
+ return JS_EXCEPTION;
+
+ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_BOUND_FUNCTION);
+ if (JS_IsException(func_obj))
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->is_constructor = JS_IsConstructor(ctx, this_val);
+ arg_count = max_int(0, argc - 1);
+ bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));
+ if (!bf)
+ goto exception;
+ bf->func_obj = JS_DupValue(ctx, this_val);
+ bf->this_val = JS_DupValue(ctx, argv[0]);
+ bf->argc = arg_count;
+ for(i = 0; i < arg_count; i++) {
+ bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);
+ }
+ p->u.bound_function = bf;
+
+ /* XXX: the spec could be simpler by only using GetOwnProperty */
+ ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);
+ if (ret < 0)
+ goto exception;
+ if (!ret) {
+ len_val = JS_NewInt32(ctx, 0);
+ } else {
+ len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);
+ if (JS_IsException(len_val))
+ goto exception;
+ if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {
+ /* most common case */
+ int len1 = JS_VALUE_GET_INT(len_val);
+ if (len1 <= arg_count)
+ len1 = 0;
+ else
+ len1 -= arg_count;
+ len_val = JS_NewInt32(ctx, len1);
+ } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {
+ double d = JS_VALUE_GET_FLOAT64(len_val);
+ if (isnan(d)) {
+ d = 0.0;
+ } else {
+ d = trunc(d);
+ if (d <= (double)arg_count)
+ d = 0.0;
+ else
+ d -= (double)arg_count; /* also converts -0 to +0 */
+ }
+ len_val = JS_NewFloat64(ctx, d);
+ } else {
+ JS_FreeValue(ctx, len_val);
+ len_val = JS_NewInt32(ctx, 0);
+ }
+ }
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
+ len_val, JS_PROP_CONFIGURABLE);
+
+ name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);
+ if (JS_IsException(name1))
+ goto exception;
+ if (!JS_IsString(name1)) {
+ JS_FreeValue(ctx, name1);
+ name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ }
+ name1 = JS_ConcatString3(ctx, "bound ", name1, "");
+ if (JS_IsException(name1))
+ goto exception;
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,
+ JS_PROP_CONFIGURABLE);
+ return func_obj;
+ exception:
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;
+
+ if (check_function(ctx, this_val))
+ return JS_EXCEPTION;
+
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (js_class_has_bytecode(p->class_id)) {
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ if (b->has_debug && b->debug.source) {
+ return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
+ }
+ func_kind = b->func_kind;
+ }
+ {
+ JSValue name;
+ const char *pref, *suff;
+
+ switch(func_kind) {
+ default:
+ case JS_FUNC_NORMAL:
+ pref = "function ";
+ break;
+ case JS_FUNC_GENERATOR:
+ pref = "function *";
+ break;
+ case JS_FUNC_ASYNC:
+ pref = "async function ";
+ break;
+ case JS_FUNC_ASYNC_GENERATOR:
+ pref = "async function *";
+ break;
+ }
+ suff = "() {\n [native code]\n}";
+ name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
+ if (JS_IsUndefined(name))
+ name = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ return JS_ConcatString3(ctx, pref, name, suff);
+ }
+}
+
+static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static const JSCFunctionListEntry js_function_proto_funcs[] = {
+ JS_CFUNC_DEF("call", 1, js_function_call ),
+ JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),
+ JS_CFUNC_DEF("bind", 1, js_function_bind ),
+ JS_CFUNC_DEF("toString", 0, js_function_toString ),
+ JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),
+ JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),
+ JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),
+};
+
+/* Error class */
+
+static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)
+{
+ JSValue iter, next_method = JS_UNDEFINED;
+ JSValue v, r = JS_UNDEFINED;
+ int64_t k;
+ BOOL done;
+
+ iter = JS_GetIterator(ctx, items, FALSE);
+ if (JS_IsException(iter))
+ goto exception;
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto exception;
+ r = JS_NewArray(ctx);
+ if (JS_IsException(r))
+ goto exception;
+ for (k = 0;; k++) {
+ v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(v))
+ goto exception_close;
+ if (done)
+ break;
+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception_close;
+ }
+ done:
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ return r;
+ exception_close:
+ JS_IteratorClose(ctx, iter, TRUE);
+ exception:
+ JS_FreeValue(ctx, r);
+ r = JS_EXCEPTION;
+ goto done;
+}
+
+static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue obj, msg, proto;
+ JSValueConst message;
+
+ if (JS_IsUndefined(new_target))
+ new_target = JS_GetActiveFunction(ctx);
+ proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
+ if (JS_IsException(proto))
+ return proto;
+ if (!JS_IsObject(proto)) {
+ JSContext *realm;
+ JSValueConst proto1;
+
+ JS_FreeValue(ctx, proto);
+ realm = JS_GetFunctionRealm(ctx, new_target);
+ if (!realm)
+ return JS_EXCEPTION;
+ if (magic < 0) {
+ proto1 = realm->class_proto[JS_CLASS_ERROR];
+ } else {
+ proto1 = realm->native_error_proto[magic];
+ }
+ proto = JS_DupValue(ctx, proto1);
+ }
+ obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);
+ JS_FreeValue(ctx, proto);
+ if (JS_IsException(obj))
+ return obj;
+ if (magic == JS_AGGREGATE_ERROR) {
+ message = argv[1];
+ } else {
+ message = argv[0];
+ }
+
+ if (!JS_IsUndefined(message)) {
+ msg = JS_ToString(ctx, message);
+ if (unlikely(JS_IsException(msg)))
+ goto exception;
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+
+ if (magic == JS_AGGREGATE_ERROR) {
+ JSValue error_list = iterator_to_array(ctx, argv[0]);
+ if (JS_IsException(error_list))
+ goto exception;
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+
+ /* skip the Error() function in the backtrace */
+ build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
+ return obj;
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue name, msg;
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
+ if (JS_IsUndefined(name))
+ name = JS_AtomToString(ctx, JS_ATOM_Error);
+ else
+ name = JS_ToStringFree(ctx, name);
+ if (JS_IsException(name))
+ return JS_EXCEPTION;
+
+ msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);
+ if (JS_IsUndefined(msg))
+ msg = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ else
+ msg = JS_ToStringFree(ctx, msg);
+ if (JS_IsException(msg)) {
+ JS_FreeValue(ctx, name);
+ return JS_EXCEPTION;
+ }
+ if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))
+ name = JS_ConcatString3(ctx, "", name, ": ");
+ return JS_ConcatString(ctx, name, msg);
+}
+
+static const JSCFunctionListEntry js_error_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_error_toString ),
+ JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+ JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+/* AggregateError */
+
+/* used by C code. */
+static JSValue js_aggregate_error_constructor(JSContext *ctx,
+ JSValueConst errors)
+{
+ JSValue obj;
+
+ obj = JS_NewObjectProtoClass(ctx,
+ ctx->native_error_proto[JS_AGGREGATE_ERROR],
+ JS_CLASS_ERROR);
+ if (JS_IsException(obj))
+ return obj;
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ return obj;
+}
+
+/* Array */
+
+static int JS_CopySubArray(JSContext *ctx,
+ JSValueConst obj, int64_t to_pos,
+ int64_t from_pos, int64_t count, int dir)
+{
+ JSObject *p;
+ int64_t i, from, to, len;
+ JSValue val;
+ int fromPresent;
+
+ p = NULL;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) {
+ p = NULL;
+ }
+ }
+
+ for (i = 0; i < count; ) {
+ if (dir < 0) {
+ from = from_pos + count - i - 1;
+ to = to_pos + count - i - 1;
+ } else {
+ from = from_pos + i;
+ to = to_pos + i;
+ }
+ if (p && p->fast_array &&
+ from >= 0 && from < (len = p->u.array.count) &&
+ to >= 0 && to < len) {
+ int64_t l, j;
+ /* Fast path for fast arrays. Since we don't look at the
+ prototype chain, we can optimize only the cases where
+ all the elements are present in the array. */
+ l = count - i;
+ if (dir < 0) {
+ l = min_int64(l, from + 1);
+ l = min_int64(l, to + 1);
+ for(j = 0; j < l; j++) {
+ set_value(ctx, &p->u.array.u.values[to - j],
+ JS_DupValue(ctx, p->u.array.u.values[from - j]));
+ }
+ } else {
+ l = min_int64(l, len - from);
+ l = min_int64(l, len - to);
+ for(j = 0; j < l; j++) {
+ set_value(ctx, &p->u.array.u.values[to + j],
+ JS_DupValue(ctx, p->u.array.u.values[from + j]));
+ }
+ }
+ i += l;
+ } else {
+ fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);
+ if (fromPresent < 0)
+ goto exception;
+
+ if (fromPresent) {
+ if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)
+ goto exception;
+ } else {
+ if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ i++;
+ }
+ }
+ return 0;
+
+ exception:
+ return -1;
+}
+
+static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ int i;
+
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);
+ if (JS_IsException(obj))
+ return obj;
+ if (argc == 1 && JS_IsNumber(argv[0])) {
+ uint32_t len;
+ if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
+ goto fail;
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
+ goto fail;
+ } else {
+ for(i = 0; i < argc; i++) {
+ if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)
+ goto fail;
+ }
+ }
+ return obj;
+fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // from(items, mapfn = void 0, this_arg = void 0)
+ JSValueConst items = argv[0], mapfn, this_arg;
+ JSValueConst args[2];
+ JSValue stack[2];
+ JSValue iter, r, v, v2, arrayLike;
+ int64_t k, len;
+ int done, mapping;
+
+ mapping = FALSE;
+ mapfn = JS_UNDEFINED;
+ this_arg = JS_UNDEFINED;
+ r = JS_UNDEFINED;
+ arrayLike = JS_UNDEFINED;
+ stack[0] = JS_UNDEFINED;
+ stack[1] = JS_UNDEFINED;
+
+ if (argc > 1) {
+ mapfn = argv[1];
+ if (!JS_IsUndefined(mapfn)) {
+ if (check_function(ctx, mapfn))
+ goto exception;
+ mapping = 1;
+ if (argc > 2)
+ this_arg = argv[2];
+ }
+ }
+ iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(iter))
+ goto exception;
+ if (!JS_IsUndefined(iter)) {
+ JS_FreeValue(ctx, iter);
+ if (JS_IsConstructor(ctx, this_val))
+ r = JS_CallConstructor(ctx, this_val, 0, NULL);
+ else
+ r = JS_NewArray(ctx);
+ if (JS_IsException(r))
+ goto exception;
+ stack[0] = JS_DupValue(ctx, items);
+ if (js_for_of_start(ctx, &stack[1], FALSE))
+ goto exception;
+ for (k = 0;; k++) {
+ v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
+ if (JS_IsException(v))
+ goto exception_close;
+ if (done)
+ break;
+ if (mapping) {
+ args[0] = v;
+ args[1] = JS_NewInt32(ctx, k);
+ v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
+ JS_FreeValue(ctx, v);
+ v = v2;
+ if (JS_IsException(v))
+ goto exception_close;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception_close;
+ }
+ } else {
+ arrayLike = JS_ToObject(ctx, items);
+ if (JS_IsException(arrayLike))
+ goto exception;
+ if (js_get_length64(ctx, &len, arrayLike) < 0)
+ goto exception;
+ v = JS_NewInt64(ctx, len);
+ args[0] = v;
+ if (JS_IsConstructor(ctx, this_val)) {
+ r = JS_CallConstructor(ctx, this_val, 1, args);
+ } else {
+ r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);
+ }
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(r))
+ goto exception;
+ for(k = 0; k < len; k++) {
+ v = JS_GetPropertyInt64(ctx, arrayLike, k);
+ if (JS_IsException(v))
+ goto exception;
+ if (mapping) {
+ args[0] = v;
+ args[1] = JS_NewInt32(ctx, k);
+ v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
+ JS_FreeValue(ctx, v);
+ v = v2;
+ if (JS_IsException(v))
+ goto exception;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
+ goto exception;
+ goto done;
+
+ exception_close:
+ if (!JS_IsUndefined(stack[0]))
+ JS_IteratorClose(ctx, stack[0], TRUE);
+ exception:
+ JS_FreeValue(ctx, r);
+ r = JS_EXCEPTION;
+ done:
+ JS_FreeValue(ctx, arrayLike);
+ JS_FreeValue(ctx, stack[0]);
+ JS_FreeValue(ctx, stack[1]);
+ return r;
+}
+
+static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, args[1];
+ int i;
+
+ if (JS_IsConstructor(ctx, this_val)) {
+ args[0] = JS_NewInt32(ctx, argc);
+ obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
+ } else {
+ obj = JS_NewArray(ctx);
+ }
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ for(i = 0; i < argc; i++) {
+ if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
+ JS_PROP_THROW) < 0) {
+ goto fail;
+ }
+ }
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_IsArray(ctx, argv[0]);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_get_this(JSContext *ctx,
+ JSValueConst this_val)
+{
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
+ JSValueConst len_val)
+{
+ JSValue ctor, ret, species;
+ int res;
+ JSContext *realm;
+
+ res = JS_IsArray(ctx, obj);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res)
+ return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
+ ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
+ if (JS_IsException(ctor))
+ return ctor;
+ if (JS_IsConstructor(ctx, ctor)) {
+ /* legacy web compatibility */
+ realm = JS_GetFunctionRealm(ctx, ctor);
+ if (!realm) {
+ JS_FreeValue(ctx, ctor);
+ return JS_EXCEPTION;
+ }
+ if (realm != ctx &&
+ js_same_value(ctx, ctor, realm->array_ctor)) {
+ JS_FreeValue(ctx, ctor);
+ ctor = JS_UNDEFINED;
+ }
+ }
+ if (JS_IsObject(ctor)) {
+ species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
+ JS_FreeValue(ctx, ctor);
+ if (JS_IsException(species))
+ return species;
+ ctor = species;
+ if (JS_IsNull(ctor))
+ ctor = JS_UNDEFINED;
+ }
+ if (JS_IsUndefined(ctor)) {
+ return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
+ } else {
+ ret = JS_CallConstructor(ctx, ctor, 1, &len_val);
+ JS_FreeValue(ctx, ctor);
+ return ret;
+ }
+}
+
+static const JSCFunctionListEntry js_array_funcs[] = {
+ JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
+ JS_CFUNC_DEF("from", 1, js_array_from ),
+ JS_CFUNC_DEF("of", 0, js_array_of ),
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+};
+
+static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
+{
+ JSValue val;
+
+ if (!JS_IsObject(obj))
+ return FALSE;
+ val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);
+ if (JS_IsException(val))
+ return -1;
+ if (!JS_IsUndefined(val))
+ return JS_ToBoolFree(ctx, val);
+ return JS_IsArray(ctx, obj);
+}
+
+static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, arr, val;
+ JSValueConst e;
+ int64_t len, k, n;
+ int i, res;
+
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ goto exception;
+
+ arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
+ if (JS_IsException(arr))
+ goto exception;
+ n = 0;
+ for (i = -1; i < argc; i++) {
+ if (i < 0)
+ e = obj;
+ else
+ e = argv[i];
+
+ res = JS_isConcatSpreadable(ctx, e);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ if (js_get_length64(ctx, &len, e))
+ goto exception;
+ if (n + len > MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array loo long");
+ goto exception;
+ }
+ for (k = 0; k < len; k++, n++) {
+ res = JS_TryGetPropertyInt64(ctx, e, k, &val);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ if (JS_DefinePropertyValueInt64(ctx, arr, n, val,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ } else {
+ if (n >= MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array loo long");
+ goto exception;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ n++;
+ }
+ }
+ if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
+ goto exception;
+
+ JS_FreeValue(ctx, obj);
+ return arr;
+
+exception:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+#define special_every 0
+#define special_some 1
+#define special_forEach 2
+#define special_map 3
+#define special_filter 4
+#define special_TA 8
+
+static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);
+
+static JSValue js_typed_array___speciesCreate(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int special)
+{
+ JSValue obj, val, index_val, res, ret;
+ JSValueConst args[3];
+ JSValueConst func, this_arg;
+ int64_t len, k, n;
+ int present;
+
+ ret = JS_UNDEFINED;
+ val = JS_UNDEFINED;
+ if (special & special_TA) {
+ obj = JS_DupValue(ctx, this_val);
+ len = js_typed_array_get_length_internal(ctx, obj);
+ if (len < 0)
+ goto exception;
+ } else {
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+ }
+ func = argv[0];
+ this_arg = JS_UNDEFINED;
+ if (argc > 1)
+ this_arg = argv[1];
+
+ if (check_function(ctx, func))
+ goto exception;
+
+ switch (special) {
+ case special_every:
+ case special_every | special_TA:
+ ret = JS_TRUE;
+ break;
+ case special_some:
+ case special_some | special_TA:
+ ret = JS_FALSE;
+ break;
+ case special_map:
+ /* XXX: JS_ArraySpeciesCreate should take int64_t */
+ ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));
+ if (JS_IsException(ret))
+ goto exception;
+ break;
+ case special_filter:
+ ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
+ if (JS_IsException(ret))
+ goto exception;
+ break;
+ case special_map | special_TA:
+ args[0] = obj;
+ args[1] = JS_NewInt32(ctx, len);
+ ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
+ if (JS_IsException(ret))
+ goto exception;
+ break;
+ case special_filter | special_TA:
+ ret = JS_NewArray(ctx);
+ if (JS_IsException(ret))
+ goto exception;
+ break;
+ }
+ n = 0;
+
+ for(k = 0; k < len; k++) {
+ if (special & special_TA) {
+ val = JS_GetPropertyInt64(ctx, obj, k);
+ if (JS_IsException(val))
+ goto exception;
+ present = TRUE;
+ } else {
+ present = JS_TryGetPropertyInt64(ctx, obj, k, &val);
+ if (present < 0)
+ goto exception;
+ }
+ if (present) {
+ index_val = JS_NewInt64(ctx, k);
+ if (JS_IsException(index_val))
+ goto exception;
+ args[0] = val;
+ args[1] = index_val;
+ args[2] = obj;
+ res = JS_Call(ctx, func, this_arg, 3, args);
+ JS_FreeValue(ctx, index_val);
+ if (JS_IsException(res))
+ goto exception;
+ switch (special) {
+ case special_every:
+ case special_every | special_TA:
+ if (!JS_ToBoolFree(ctx, res)) {
+ ret = JS_FALSE;
+ goto done;
+ }
+ break;
+ case special_some:
+ case special_some | special_TA:
+ if (JS_ToBoolFree(ctx, res)) {
+ ret = JS_TRUE;
+ goto done;
+ }
+ break;
+ case special_map:
+ if (JS_DefinePropertyValueInt64(ctx, ret, k, res,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ break;
+ case special_map | special_TA:
+ if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)
+ goto exception;
+ break;
+ case special_filter:
+ case special_filter | special_TA:
+ if (JS_ToBoolFree(ctx, res)) {
+ if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ break;
+ default:
+ JS_FreeValue(ctx, res);
+ break;
+ }
+ JS_FreeValue(ctx, val);
+ val = JS_UNDEFINED;
+ }
+ }
+done:
+ if (special == (special_filter | special_TA)) {
+ JSValue arr;
+ args[0] = obj;
+ args[1] = JS_NewInt32(ctx, n);
+ arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
+ if (JS_IsException(arr))
+ goto exception;
+ args[0] = ret;
+ res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);
+ if (check_exception_free(ctx, res))
+ goto exception;
+ JS_FreeValue(ctx, ret);
+ ret = arr;
+ }
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return ret;
+
+exception:
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+#define special_reduce 0
+#define special_reduceRight 1
+
+static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int special)
+{
+ JSValue obj, val, index_val, acc, acc1;
+ JSValueConst args[4];
+ JSValueConst func;
+ int64_t len, k, k1;
+ int present;
+
+ acc = JS_UNDEFINED;
+ val = JS_UNDEFINED;
+ if (special & special_TA) {
+ obj = JS_DupValue(ctx, this_val);
+ len = js_typed_array_get_length_internal(ctx, obj);
+ if (len < 0)
+ goto exception;
+ } else {
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+ }
+ func = argv[0];
+
+ if (check_function(ctx, func))
+ goto exception;
+
+ k = 0;
+ if (argc > 1) {
+ acc = JS_DupValue(ctx, argv[1]);
+ } else {
+ for(;;) {
+ if (k >= len) {
+ JS_ThrowTypeError(ctx, "empty array");
+ goto exception;
+ }
+ k1 = (special & special_reduceRight) ? len - k - 1 : k;
+ k++;
+ if (special & special_TA) {
+ acc = JS_GetPropertyInt64(ctx, obj, k1);
+ if (JS_IsException(acc))
+ goto exception;
+ break;
+ } else {
+ present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);
+ if (present < 0)
+ goto exception;
+ if (present)
+ break;
+ }
+ }
+ }
+ for (; k < len; k++) {
+ k1 = (special & special_reduceRight) ? len - k - 1 : k;
+ if (special & special_TA) {
+ val = JS_GetPropertyInt64(ctx, obj, k1);
+ if (JS_IsException(val))
+ goto exception;
+ present = TRUE;
+ } else {
+ present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);
+ if (present < 0)
+ goto exception;
+ }
+ if (present) {
+ index_val = JS_NewInt64(ctx, k1);
+ if (JS_IsException(index_val))
+ goto exception;
+ args[0] = acc;
+ args[1] = val;
+ args[2] = index_val;
+ args[3] = obj;
+ acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);
+ JS_FreeValue(ctx, index_val);
+ JS_FreeValue(ctx, val);
+ val = JS_UNDEFINED;
+ if (JS_IsException(acc1))
+ goto exception;
+ JS_FreeValue(ctx, acc);
+ acc = acc1;
+ }
+ }
+ JS_FreeValue(ctx, obj);
+ return acc;
+
+exception:
+ JS_FreeValue(ctx, acc);
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ int64_t len, start, end;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ start = 0;
+ if (argc > 1 && !JS_IsUndefined(argv[1])) {
+ if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))
+ goto exception;
+ }
+
+ end = len;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))
+ goto exception;
+ }
+
+ /* XXX: should special case fast arrays */
+ while (start < end) {
+ if (JS_SetPropertyInt64(ctx, obj, start,
+ JS_DupValue(ctx, argv[0])) < 0)
+ goto exception;
+ start++;
+ }
+ return obj;
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, val;
+ int64_t len, n, res;
+ JSValue *arrp;
+ uint32_t count;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ res = FALSE;
+ if (len > 0) {
+ n = 0;
+ if (argc > 1) {
+ if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
+ goto exception;
+ }
+ if (js_get_fast_array(ctx, obj, &arrp, &count)) {
+ for (; n < count; n++) {
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
+ JS_DupValue(ctx, arrp[n]),
+ JS_EQ_SAME_VALUE_ZERO)) {
+ res = TRUE;
+ goto done;
+ }
+ }
+ }
+ for (; n < len; n++) {
+ val = JS_GetPropertyInt64(ctx, obj, n);
+ if (JS_IsException(val))
+ goto exception;
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,
+ JS_EQ_SAME_VALUE_ZERO)) {
+ res = TRUE;
+ break;
+ }
+ }
+ }
+ done:
+ JS_FreeValue(ctx, obj);
+ return JS_NewBool(ctx, res);
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, val;
+ int64_t len, n, res;
+ JSValue *arrp;
+ uint32_t count;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ res = -1;
+ if (len > 0) {
+ n = 0;
+ if (argc > 1) {
+ if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
+ goto exception;
+ }
+ if (js_get_fast_array(ctx, obj, &arrp, &count)) {
+ for (; n < count; n++) {
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
+ JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {
+ res = n;
+ goto done;
+ }
+ }
+ }
+ for (; n < len; n++) {
+ int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
+ if (present < 0)
+ goto exception;
+ if (present) {
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
+ res = n;
+ break;
+ }
+ }
+ }
+ }
+ done:
+ JS_FreeValue(ctx, obj);
+ return JS_NewInt64(ctx, res);
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, val;
+ int64_t len, n, res;
+ int present;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ res = -1;
+ if (len > 0) {
+ n = len - 1;
+ if (argc > 1) {
+ if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))
+ goto exception;
+ }
+ /* XXX: should special case fast arrays */
+ for (; n >= 0; n--) {
+ present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
+ if (present < 0)
+ goto exception;
+ if (present) {
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
+ res = n;
+ break;
+ }
+ }
+ }
+ }
+ JS_FreeValue(ctx, obj);
+ return JS_NewInt64(ctx, res);
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int findIndex)
+{
+ JSValueConst func, this_arg;
+ JSValueConst args[3];
+ JSValue obj, val, index_val, res;
+ int64_t len, k;
+
+ index_val = JS_UNDEFINED;
+ val = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ func = argv[0];
+ if (check_function(ctx, func))
+ goto exception;
+
+ this_arg = JS_UNDEFINED;
+ if (argc > 1)
+ this_arg = argv[1];
+
+ for(k = 0; k < len; k++) {
+ index_val = JS_NewInt64(ctx, k);
+ if (JS_IsException(index_val))
+ goto exception;
+ val = JS_GetPropertyValue(ctx, obj, index_val);
+ if (JS_IsException(val))
+ goto exception;
+ args[0] = val;
+ args[1] = index_val;
+ args[2] = this_val;
+ res = JS_Call(ctx, func, this_arg, 3, args);
+ if (JS_IsException(res))
+ goto exception;
+ if (JS_ToBoolFree(ctx, res)) {
+ if (findIndex) {
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return index_val;
+ } else {
+ JS_FreeValue(ctx, index_val);
+ JS_FreeValue(ctx, obj);
+ return val;
+ }
+ }
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, index_val);
+ }
+ JS_FreeValue(ctx, obj);
+ if (findIndex)
+ return JS_NewInt32(ctx, -1);
+ else
+ return JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, index_val);
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, method, ret;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ method = JS_GetProperty(ctx, obj, JS_ATOM_join);
+ if (JS_IsException(method)) {
+ ret = JS_EXCEPTION;
+ } else if (!JS_IsFunction(ctx, method)) {
+ /* Use intrinsic Object.prototype.toString */
+ JS_FreeValue(ctx, method);
+ ret = js_object_toString(ctx, obj, 0, NULL);
+ } else {
+ ret = JS_CallFree(ctx, method, obj, 0, NULL);
+ }
+ JS_FreeValue(ctx, obj);
+ return ret;
+}
+
+static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int toLocaleString)
+{
+ JSValue obj, sep = JS_UNDEFINED, el;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p = NULL;
+ int64_t i, n;
+ int c;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &n, obj))
+ goto exception;
+
+ c = ','; /* default separator */
+ if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
+ sep = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(sep))
+ goto exception;
+ p = JS_VALUE_GET_STRING(sep);
+ if (p->len == 1 && !p->is_wide_char)
+ c = p->u.str8[0];
+ else
+ c = -1;
+ }
+ string_buffer_init(ctx, b, 0);
+
+ for(i = 0; i < n; i++) {
+ if (i > 0) {
+ if (c >= 0) {
+ string_buffer_putc8(b, c);
+ } else {
+ string_buffer_concat(b, p, 0, p->len);
+ }
+ }
+ el = JS_GetPropertyUint32(ctx, obj, i);
+ if (JS_IsException(el))
+ goto fail;
+ if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
+ if (toLocaleString) {
+ el = JS_ToLocaleStringFree(ctx, el);
+ }
+ if (string_buffer_concat_value_free(b, el))
+ goto fail;
+ }
+ }
+ JS_FreeValue(ctx, sep);
+ JS_FreeValue(ctx, obj);
+ return string_buffer_end(b);
+
+fail:
+ string_buffer_free(b);
+ JS_FreeValue(ctx, sep);
+exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int shift)
+{
+ JSValue obj, res = JS_UNDEFINED;
+ int64_t len, newLen;
+ JSValue *arrp;
+ uint32_t count32;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+ newLen = 0;
+ if (len > 0) {
+ newLen = len - 1;
+ /* Special case fast arrays */
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ if (shift) {
+ res = arrp[0];
+ memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));
+ p->u.array.count--;
+ } else {
+ res = arrp[count32 - 1];
+ p->u.array.count--;
+ }
+ } else {
+ if (shift) {
+ res = JS_GetPropertyInt64(ctx, obj, 0);
+ if (JS_IsException(res))
+ goto exception;
+ if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))
+ goto exception;
+ } else {
+ res = JS_GetPropertyInt64(ctx, obj, newLen);
+ if (JS_IsException(res))
+ goto exception;
+ }
+ if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
+ goto exception;
+
+ JS_FreeValue(ctx, obj);
+ return res;
+
+ exception:
+ JS_FreeValue(ctx, res);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int unshift)
+{
+ JSValue obj;
+ int i;
+ int64_t len, from, newLen;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+ newLen = len + argc;
+ if (newLen > MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array loo long");
+ goto exception;
+ }
+ from = len;
+ if (unshift && argc > 0) {
+ if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))
+ goto exception;
+ from = 0;
+ }
+ for(i = 0; i < argc; i++) {
+ if (JS_SetPropertyInt64(ctx, obj, from + i,
+ JS_DupValue(ctx, argv[i])) < 0)
+ goto exception;
+ }
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
+ goto exception;
+
+ JS_FreeValue(ctx, obj);
+ return JS_NewInt64(ctx, newLen);
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, lval, hval;
+ JSValue *arrp;
+ int64_t len, l, h;
+ int l_present, h_present;
+ uint32_t count32;
+
+ lval = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ /* Special case fast arrays */
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
+ uint32_t ll, hh;
+
+ if (count32 > 1) {
+ for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {
+ lval = arrp[ll];
+ arrp[ll] = arrp[hh];
+ arrp[hh] = lval;
+ }
+ }
+ return obj;
+ }
+
+ for (l = 0, h = len - 1; l < h; l++, h--) {
+ l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);
+ if (l_present < 0)
+ goto exception;
+ h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);
+ if (h_present < 0)
+ goto exception;
+ if (h_present) {
+ if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)
+ goto exception;
+
+ if (l_present) {
+ if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
+ lval = JS_UNDEFINED;
+ goto exception;
+ }
+ lval = JS_UNDEFINED;
+ } else {
+ if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ } else {
+ if (l_present) {
+ if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)
+ goto exception;
+ if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
+ lval = JS_UNDEFINED;
+ goto exception;
+ }
+ lval = JS_UNDEFINED;
+ }
+ }
+ }
+ return obj;
+
+ exception:
+ JS_FreeValue(ctx, lval);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int splice)
+{
+ JSValue obj, arr, val, len_val;
+ int64_t len, start, k, final, n, count, del_count, new_len;
+ int kPresent;
+ JSValue *arrp;
+ uint32_t count32, i, item_count;
+
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
+ goto exception;
+
+ if (splice) {
+ if (argc == 0) {
+ item_count = 0;
+ del_count = 0;
+ } else if (argc == 1) {
+ item_count = 0;
+ del_count = len - start;
+ } else {
+ item_count = argc - 2;
+ if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))
+ goto exception;
+ }
+ if (len + item_count - del_count > MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array loo long");
+ goto exception;
+ }
+ count = del_count;
+ } else {
+ item_count = 0; /* avoid warning */
+ final = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))
+ goto exception;
+ }
+ count = max_int64(final - start, 0);
+ }
+ len_val = JS_NewInt64(ctx, count);
+ arr = JS_ArraySpeciesCreate(ctx, obj, len_val);
+ JS_FreeValue(ctx, len_val);
+ if (JS_IsException(arr))
+ goto exception;
+
+ k = start;
+ final = start + count;
+ n = 0;
+ /* The fast array test on arr ensures that
+ JS_CreateDataPropertyUint32() won't modify obj in case arr is
+ an exotic object */
+ /* Special case fast arrays */
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) &&
+ js_is_fast_array(ctx, arr)) {
+ /* XXX: should share code with fast array constructor */
+ for (; k < final && k < count32; k++, n++) {
+ if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ /* Copy the remaining elements if any (handle case of inherited properties) */
+ for (; k < final; k++, n++) {
+ kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
+ if (kPresent < 0)
+ goto exception;
+ if (kPresent) {
+ if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
+ goto exception;
+
+ if (splice) {
+ new_len = len + item_count - del_count;
+ if (item_count != del_count) {
+ if (JS_CopySubArray(ctx, obj, start + item_count,
+ start + del_count, len - (start + del_count),
+ item_count <= del_count ? +1 : -1) < 0)
+ goto exception;
+
+ for (k = len; k-- > new_len; ) {
+ if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ for (i = 0; i < item_count; i++) {
+ if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)
+ goto exception;
+ }
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)
+ goto exception;
+ }
+ JS_FreeValue(ctx, obj);
+ return arr;
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ int64_t len, from, to, final, count;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))
+ goto exception;
+
+ if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))
+ goto exception;
+
+ final = len;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))
+ goto exception;
+ }
+
+ count = min_int64(final - from, len - to);
+
+ if (JS_CopySubArray(ctx, obj, to, from, count,
+ (from < to && to < from + count) ? -1 : +1))
+ goto exception;
+
+ return obj;
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
+ JSValueConst source, int64_t sourceLen,
+ int64_t targetIndex, int depth,
+ JSValueConst mapperFunction,
+ JSValueConst thisArg)
+{
+ JSValue element;
+ int64_t sourceIndex, elementLen;
+ int present, is_array;
+
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ JS_ThrowStackOverflow(ctx);
+ return -1;
+ }
+
+ for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
+ present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);
+ if (present < 0)
+ return -1;
+ if (!present)
+ continue;
+ if (!JS_IsUndefined(mapperFunction)) {
+ JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
+ element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ if (JS_IsException(element))
+ return -1;
+ }
+ if (depth > 0) {
+ is_array = JS_IsArray(ctx, element);
+ if (is_array < 0)
+ goto fail;
+ if (is_array) {
+ if (js_get_length64(ctx, &elementLen, element) < 0)
+ goto fail;
+ targetIndex = JS_FlattenIntoArray(ctx, target, element,
+ elementLen, targetIndex,
+ depth - 1,
+ JS_UNDEFINED, JS_UNDEFINED);
+ if (targetIndex < 0)
+ goto fail;
+ JS_FreeValue(ctx, element);
+ continue;
+ }
+ }
+ if (targetIndex >= MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array too long");
+ goto fail;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ return -1;
+ targetIndex++;
+ }
+ return targetIndex;
+
+fail:
+ JS_FreeValue(ctx, element);
+ return -1;
+}
+
+static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int map)
+{
+ JSValue obj, arr;
+ JSValueConst mapperFunction, thisArg;
+ int64_t sourceLen;
+ int depthNum;
+
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &sourceLen, obj))
+ goto exception;
+
+ depthNum = 1;
+ mapperFunction = JS_UNDEFINED;
+ thisArg = JS_UNDEFINED;
+ if (map) {
+ mapperFunction = argv[0];
+ if (argc > 1) {
+ thisArg = argv[1];
+ }
+ if (check_function(ctx, mapperFunction))
+ goto exception;
+ } else {
+ if (argc > 0 && !JS_IsUndefined(argv[0])) {
+ if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)
+ goto exception;
+ }
+ }
+ arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
+ if (JS_IsException(arr))
+ goto exception;
+ if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,
+ mapperFunction, thisArg) < 0)
+ goto exception;
+ JS_FreeValue(ctx, obj);
+ return arr;
+
+exception:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+/* Array sort */
+
+typedef struct ValueSlot {
+ JSValue val;
+ JSString *str;
+ int64_t pos;
+} ValueSlot;
+
+struct array_sort_context {
+ JSContext *ctx;
+ int exception;
+ int has_method;
+ JSValueConst method;
+};
+
+static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {
+ struct array_sort_context *psc = opaque;
+ JSContext *ctx = psc->ctx;
+ JSValueConst argv[2];
+ JSValue res;
+ ValueSlot *ap = (ValueSlot *)(void *)a;
+ ValueSlot *bp = (ValueSlot *)(void *)b;
+ int cmp;
+
+ if (psc->exception)
+ return 0;
+
+ if (psc->has_method) {
+ /* custom sort function is specified as returning 0 for identical
+ * objects: avoid method call overhead.
+ */
+ if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))
+ goto cmp_same;
+ argv[0] = ap->val;
+ argv[1] = bp->val;
+ res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);
+ if (JS_IsException(res))
+ goto exception;
+ if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
+ int val = JS_VALUE_GET_INT(res);
+ cmp = (val > 0) - (val < 0);
+ } else {
+ double val;
+ if (JS_ToFloat64Free(ctx, &val, res) < 0)
+ goto exception;
+ cmp = (val > 0) - (val < 0);
+ }
+ } else {
+ /* Not supposed to bypass ToString even for identical objects as
+ * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js
+ */
+ if (!ap->str) {
+ JSValue str = JS_ToString(ctx, ap->val);
+ if (JS_IsException(str))
+ goto exception;
+ ap->str = JS_VALUE_GET_STRING(str);
+ }
+ if (!bp->str) {
+ JSValue str = JS_ToString(ctx, bp->val);
+ if (JS_IsException(str))
+ goto exception;
+ bp->str = JS_VALUE_GET_STRING(str);
+ }
+ cmp = js_string_compare(ctx, ap->str, bp->str);
+ }
+ if (cmp != 0)
+ return cmp;
+cmp_same:
+ /* make sort stable: compare array offsets */
+ return (ap->pos > bp->pos) - (ap->pos < bp->pos);
+
+exception:
+ psc->exception = 1;
+ return 0;
+}
+
+static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ struct array_sort_context asc = { ctx, 0, 0, argv[0] };
+ JSValue obj = JS_UNDEFINED;
+ ValueSlot *array = NULL;
+ size_t array_size = 0, pos = 0, n = 0;
+ int64_t i, len, undefined_count = 0;
+ int present;
+
+ if (!JS_IsUndefined(asc.method)) {
+ if (check_function(ctx, asc.method))
+ goto exception;
+ asc.has_method = 1;
+ }
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ /* XXX: should special case fast arrays */
+ for (i = 0; i < len; i++) {
+ if (pos >= array_size) {
+ size_t new_size, slack;
+ ValueSlot *new_array;
+ new_size = (array_size + (array_size >> 1) + 31) & ~15;
+ new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);
+ if (new_array == NULL)
+ goto exception;
+ new_size += slack / sizeof(*new_array);
+ array = new_array;
+ array_size = new_size;
+ }
+ present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);
+ if (present < 0)
+ goto exception;
+ if (present == 0)
+ continue;
+ if (JS_IsUndefined(array[pos].val)) {
+ undefined_count++;
+ continue;
+ }
+ array[pos].str = NULL;
+ array[pos].pos = i;
+ pos++;
+ }
+ rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);
+ if (asc.exception)
+ goto exception;
+
+ /* XXX: should special case fast arrays */
+ while (n < pos) {
+ if (array[n].str)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
+ if (array[n].pos == n) {
+ JS_FreeValue(ctx, array[n].val);
+ } else {
+ if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {
+ n++;
+ goto exception;
+ }
+ }
+ n++;
+ }
+ js_free(ctx, array);
+ for (i = n; undefined_count-- > 0; i++) {
+ if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)
+ goto fail;
+ }
+ for (; i < len; i++) {
+ if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)
+ goto fail;
+ }
+ return obj;
+
+exception:
+ for (; n < pos; n++) {
+ JS_FreeValue(ctx, array[n].val);
+ if (array[n].str)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
+ }
+ js_free(ctx, array);
+fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+typedef struct JSArrayIteratorData {
+ JSValue obj;
+ JSIteratorKindEnum kind;
+ uint32_t idx;
+} JSArrayIteratorData;
+
+static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSArrayIteratorData *it = p->u.array_iterator_data;
+ if (it) {
+ JS_FreeValueRT(rt, it->obj);
+ js_free_rt(rt, it);
+ }
+}
+
+static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSArrayIteratorData *it = p->u.array_iterator_data;
+ if (it) {
+ JS_MarkValue(rt, it->obj, mark_func);
+ }
+}
+
+static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)
+{
+ JSValue obj;
+ int i;
+
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ for(i = 0; i < len; i++) {
+ if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ }
+ return obj;
+}
+
+static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue enum_obj, arr;
+ JSArrayIteratorData *it;
+ JSIteratorKindEnum kind;
+ int class_id;
+
+ kind = magic & 3;
+ if (magic & 4) {
+ /* string iterator case */
+ arr = JS_ToStringCheckObject(ctx, this_val);
+ class_id = JS_CLASS_STRING_ITERATOR;
+ } else {
+ arr = JS_ToObject(ctx, this_val);
+ class_id = JS_CLASS_ARRAY_ITERATOR;
+ }
+ if (JS_IsException(arr))
+ goto fail;
+ enum_obj = JS_NewObjectClass(ctx, class_id);
+ if (JS_IsException(enum_obj))
+ goto fail;
+ it = js_malloc(ctx, sizeof(*it));
+ if (!it)
+ goto fail1;
+ it->obj = arr;
+ it->kind = kind;
+ it->idx = 0;
+ JS_SetOpaque(enum_obj, it);
+ return enum_obj;
+ fail1:
+ JS_FreeValue(ctx, enum_obj);
+ fail:
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSArrayIteratorData *it;
+ uint32_t len, idx;
+ JSValue val, obj;
+ JSObject *p;
+
+ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);
+ if (!it)
+ goto fail1;
+ if (JS_IsUndefined(it->obj))
+ goto done;
+ p = JS_VALUE_GET_OBJ(it->obj);
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ if (typed_array_is_detached(ctx, p)) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail1;
+ }
+ len = p->u.array.count;
+ } else {
+ if (js_get_length32(ctx, &len, it->obj)) {
+ fail1:
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+ }
+ }
+ idx = it->idx;
+ if (idx >= len) {
+ JS_FreeValue(ctx, it->obj);
+ it->obj = JS_UNDEFINED;
+ done:
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ }
+ it->idx = idx + 1;
+ *pdone = FALSE;
+ if (it->kind == JS_ITERATOR_KIND_KEY) {
+ return JS_NewUint32(ctx, idx);
+ } else {
+ val = JS_GetPropertyUint32(ctx, it->obj, idx);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ if (it->kind == JS_ITERATOR_KIND_VALUE) {
+ return val;
+ } else {
+ JSValueConst args[2];
+ JSValue num;
+ num = JS_NewUint32(ctx, idx);
+ args[0] = num;
+ args[1] = val;
+ obj = js_create_array(ctx, 2, args);
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, num);
+ return obj;
+ }
+ }
+}
+
+static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_DupValue(ctx, this_val);
+}
+
+static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
+ JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
+};
+
+static const JSCFunctionListEntry js_array_proto_funcs[] = {
+ JS_CFUNC_DEF("concat", 1, js_array_concat ),
+ JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
+ JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),
+ JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),
+ JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),
+ JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),
+ JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),
+ JS_CFUNC_DEF("fill", 1, js_array_fill ),
+ JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ),
+ JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ),
+ JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),
+ JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),
+ JS_CFUNC_DEF("includes", 1, js_array_includes ),
+ JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),
+ JS_CFUNC_DEF("toString", 0, js_array_toString ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),
+ JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),
+ JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
+ JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
+ JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
+ JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
+ JS_CFUNC_DEF("sort", 1, js_array_sort ),
+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),
+ JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),
+ JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
+ JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
+ JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),
+ JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
+ JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),
+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
+};
+
+static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+/* Number */
+
+static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, obj;
+ if (argc == 0) {
+ val = JS_NewInt32(ctx, 0);
+ } else {
+ val = JS_ToNumeric(ctx, argv[0]);
+ if (JS_IsException(val))
+ return val;
+ switch(JS_VALUE_GET_TAG(val)) {
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ double d;
+ bf_get_float64(&p->num, &d, BF_RNDN);
+ JS_FreeValue(ctx, val);
+ val = __JS_NewFloat64(ctx, d);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ return val;
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return val;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ if (!JS_IsUndefined(new_target)) {
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
+ if (!JS_IsException(obj))
+ JS_SetObjectData(ctx, obj, val);
+ return obj;
+ } else {
+ return val;
+ }
+}
+
+#if 0
+static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));
+}
+
+static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int64_t v;
+ if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))
+ return JS_EXCEPTION;
+ return JS_NewInt64(ctx, v);
+}
+#endif
+
+static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (!JS_IsNumber(argv[0]))
+ return JS_FALSE;
+ return js_global_isNaN(ctx, this_val, argc, argv);
+}
+
+static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (!JS_IsNumber(argv[0]))
+ return JS_FALSE;
+ return js_global_isFinite(ctx, this_val, argc, argv);
+}
+
+static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_NumberIsInteger(ctx, argv[0]);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double d;
+ if (!JS_IsNumber(argv[0]))
+ return JS_FALSE;
+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
+ return JS_EXCEPTION;
+ return JS_NewBool(ctx, is_safe_integer(d));
+}
+
+static const JSCFunctionListEntry js_number_funcs[] = {
+ /* global ParseInt and parseFloat should be defined already or delayed */
+ JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),
+ JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),
+ JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),
+ JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),
+ JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),
+ JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),
+ JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
+ JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
+ JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
+ JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
+ JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
+ JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
+ JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
+ JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
+ //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),
+ //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),
+};
+
+static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_IsNumber(this_val))
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_NUMBER) {
+ if (JS_IsNumber(p->u.object_data))
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a number");
+}
+
+static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisNumberValue(ctx, this_val);
+}
+
+static int js_get_radix(JSContext *ctx, JSValueConst val)
+{
+ int radix;
+ if (JS_ToInt32Sat(ctx, &radix, val))
+ return -1;
+ if (radix < 2 || radix > 36) {
+ JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
+ return -1;
+ }
+ return radix;
+}
+
+static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue val;
+ int base;
+ double d;
+
+ val = js_thisNumberValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (magic || JS_IsUndefined(argv[0])) {
+ base = 10;
+ } else {
+ base = js_get_radix(ctx, argv[0]);
+ if (base < 0)
+ goto fail;
+ }
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return JS_EXCEPTION;
+ return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int f;
+ double d;
+
+ val = js_thisNumberValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return JS_EXCEPTION;
+ if (JS_ToInt32Sat(ctx, &f, argv[0]))
+ return JS_EXCEPTION;
+ if (f < 0 || f > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+ if (fabs(d) >= 1e21) {
+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ } else {
+ return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);
+ }
+}
+
+static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int f, flags;
+ double d;
+
+ val = js_thisNumberValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return JS_EXCEPTION;
+ if (JS_ToInt32Sat(ctx, &f, argv[0]))
+ return JS_EXCEPTION;
+ if (!isfinite(d)) {
+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ }
+ if (JS_IsUndefined(argv[0])) {
+ flags = 0;
+ f = 0;
+ } else {
+ if (f < 0 || f > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+ f++;
+ flags = JS_DTOA_FIXED_FORMAT;
+ }
+ return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);
+}
+
+static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int p;
+ double d;
+
+ val = js_thisNumberValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return JS_EXCEPTION;
+ if (JS_IsUndefined(argv[0]))
+ goto to_string;
+ if (JS_ToInt32Sat(ctx, &p, argv[0]))
+ return JS_EXCEPTION;
+ if (!isfinite(d)) {
+ to_string:
+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ }
+ if (p < 1 || p > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+ return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);
+}
+
+static const JSCFunctionListEntry js_number_proto_funcs[] = {
+ JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),
+ JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),
+ JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),
+ JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),
+ JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),
+};
+
+static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *str, *p;
+ int radix, flags;
+ JSValue ret;
+
+ str = JS_ToCString(ctx, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &radix, argv[1])) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ if (radix != 0 && (radix < 2 || radix > 36)) {
+ ret = JS_NAN;
+ } else {
+ p = str;
+ p += skip_spaces(p);
+ flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;
+ ret = js_atof(ctx, p, NULL, radix, flags);
+ }
+ JS_FreeCString(ctx, str);
+ return ret;
+}
+
+static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *str, *p;
+ JSValue ret;
+
+ str = JS_ToCString(ctx, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ ret = js_atof(ctx, p, NULL, 10, 0);
+ JS_FreeCString(ctx, str);
+ return ret;
+}
+
+/* Boolean */
+static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, obj;
+ val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));
+ if (!JS_IsUndefined(new_target)) {
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);
+ if (!JS_IsException(obj))
+ JS_SetObjectData(ctx, obj, val);
+ return obj;
+ } else {
+ return val;
+ }
+}
+
+static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_BOOLEAN) {
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)
+ return p->u.object_data;
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a boolean");
+}
+
+static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val = js_thisBooleanValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
+ JS_ATOM_true : JS_ATOM_false);
+}
+
+static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisBooleanValue(ctx, this_val);
+}
+
+static const JSCFunctionListEntry js_boolean_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_boolean_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),
+};
+
+/* String */
+
+static int js_string_get_own_property(JSContext *ctx,
+ JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop)
+{
+ JSObject *p;
+ JSString *p1;
+ uint32_t idx, ch;
+
+ /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
+ if (JS_AtomIsTaggedInt(prop)) {
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
+ p1 = JS_VALUE_GET_STRING(p->u.object_data);
+ idx = JS_AtomToUInt32(prop);
+ if (idx < p1->len) {
+ if (desc) {
+ if (p1->is_wide_char)
+ ch = p1->u.str16[idx];
+ else
+ ch = p1->u.str8[idx];
+ desc->flags = JS_PROP_ENUMERABLE;
+ desc->value = js_new_string_char(ctx, ch);
+ desc->getter = JS_UNDEFINED;
+ desc->setter = JS_UNDEFINED;
+ }
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static int js_string_define_own_property(JSContext *ctx,
+ JSValueConst this_obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter,
+ JSValueConst setter, int flags)
+{
+ uint32_t idx;
+ JSObject *p;
+ JSString *p1, *p2;
+
+ if (JS_AtomIsTaggedInt(prop)) {
+ idx = JS_AtomToUInt32(prop);
+ p = JS_VALUE_GET_OBJ(this_obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
+ goto def;
+ p1 = JS_VALUE_GET_STRING(p->u.object_data);
+ if (idx >= p1->len)
+ goto def;
+ if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))
+ goto fail;
+ /* check that the same value is configured */
+ if (flags & JS_PROP_HAS_VALUE) {
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
+ goto fail;
+ p2 = JS_VALUE_GET_STRING(val);
+ if (p2->len != 1)
+ goto fail;
+ if (string_get(p1, idx) != string_get(p2, 0)) {
+ fail:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
+ }
+ }
+ return TRUE;
+ } else {
+ def:
+ return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
+ flags | JS_PROP_NO_EXOTIC);
+ }
+}
+
+static int js_string_delete_property(JSContext *ctx,
+ JSValueConst obj, JSAtom prop)
+{
+ uint32_t idx;
+
+ if (JS_AtomIsTaggedInt(prop)) {
+ idx = JS_AtomToUInt32(prop);
+ if (idx < js_string_obj_get_length(ctx, obj)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static const JSClassExoticMethods js_string_exotic_methods = {
+ .get_own_property = js_string_get_own_property,
+ .define_own_property = js_string_define_own_property,
+ .delete_property = js_string_delete_property,
+};
+
+static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, obj;
+ if (argc == 0) {
+ val = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ } else {
+ if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);
+ val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");
+ } else {
+ val = JS_ToString(ctx, argv[0]);
+ }
+ if (JS_IsException(val))
+ return val;
+ }
+ if (!JS_IsUndefined(new_target)) {
+ JSString *p1 = JS_VALUE_GET_STRING(val);
+
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);
+ if (!JS_IsException(obj)) {
+ JS_SetObjectData(ctx, obj, val);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
+ }
+ return obj;
+ } else {
+ return val;
+ }
+}
+
+static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_STRING) {
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a string");
+}
+
+static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int i;
+ StringBuffer b_s, *b = &b_s;
+
+ string_buffer_init(ctx, b, argc);
+
+ for(i = 0; i < argc; i++) {
+ int32_t c;
+ if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+ }
+ }
+ return string_buffer_end(b);
+}
+
+static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double d;
+ int i, c;
+ StringBuffer b_s, *b = &b_s;
+
+ /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */
+
+ if (string_buffer_init(ctx, b, argc))
+ goto fail;
+ for(i = 0; i < argc; i++) {
+ if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {
+ c = JS_VALUE_GET_INT(argv[i]);
+ if (c < 0 || c > 0x10ffff)
+ goto range_error;
+ } else {
+ if (JS_ToFloat64(ctx, &d, argv[i]))
+ goto fail;
+ if (d < 0 || d > 0x10ffff || (c = (int)d) != d)
+ goto range_error;
+ }
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ return string_buffer_end(b);
+
+ range_error:
+ JS_ThrowRangeError(ctx, "invalid code point");
+ fail:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // raw(temp,...a)
+ JSValue cooked, val, raw;
+ StringBuffer b_s, *b = &b_s;
+ int64_t i, n;
+
+ string_buffer_init(ctx, b, 0);
+ raw = JS_UNDEFINED;
+ cooked = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(cooked))
+ goto exception;
+ raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));
+ if (JS_IsException(raw))
+ goto exception;
+ if (js_get_length64(ctx, &n, raw) < 0)
+ goto exception;
+
+ for (i = 0; i < n; i++) {
+ val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));
+ if (JS_IsException(val))
+ goto exception;
+ string_buffer_concat_value_free(b, val);
+ if (i < n - 1 && i + 1 < argc) {
+ if (string_buffer_concat_value(b, argv[i + 1]))
+ goto exception;
+ }
+ }
+ JS_FreeValue(ctx, cooked);
+ JS_FreeValue(ctx, raw);
+ return string_buffer_end(b);
+
+exception:
+ JS_FreeValue(ctx, cooked);
+ JS_FreeValue(ctx, raw);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+/* only used in test262 */
+JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint32_t start, end, i, n;
+ StringBuffer b_s, *b = &b_s;
+
+ if (JS_ToUint32(ctx, &start, argv[0]) ||
+ JS_ToUint32(ctx, &end, argv[1]))
+ return JS_EXCEPTION;
+ end = min_uint32(end, 0x10ffff + 1);
+
+ if (start > end) {
+ start = end;
+ }
+ n = end - start;
+ if (end > 0x10000) {
+ n += end - max_uint32(start, 0x10000);
+ }
+ if (string_buffer_init2(ctx, b, n, end >= 0x100))
+ return JS_EXCEPTION;
+ for(i = start; i < end; i++) {
+ string_buffer_putc(b, i);
+ }
+ return string_buffer_end(b);
+}
+
+#if 0
+static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int c;
+ if (JS_ToInt32(ctx, &c, argv[0]))
+ return JS_EXCEPTION;
+ return JS_NewBool(ctx, lre_is_space(c));
+}
+#endif
+
+static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ JSString *p;
+ int idx, c;
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ if (idx < 0 || idx >= p->len) {
+ ret = JS_NAN;
+ } else {
+ if (p->is_wide_char)
+ c = p->u.str16[idx];
+ else
+ c = p->u.str8[idx];
+ ret = JS_NewInt32(ctx, c);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ JSString *p;
+ int idx, c;
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ if (idx < 0 || idx >= p->len) {
+ ret = js_new_string8(ctx, NULL, 0);
+ } else {
+ if (p->is_wide_char)
+ c = p->u.str16[idx];
+ else
+ c = p->u.str8[idx];
+ ret = js_new_string_char(ctx, c);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ JSString *p;
+ int idx, c;
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ if (idx < 0 || idx >= p->len) {
+ ret = JS_UNDEFINED;
+ } else {
+ c = string_getc(p, &idx);
+ ret = JS_NewInt32(ctx, c);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue r;
+ int i;
+
+ /* XXX: Use more efficient method */
+ /* XXX: This method is OK if r has a single refcount */
+ /* XXX: should use string_buffer? */
+ r = JS_ToStringCheckObject(ctx, this_val);
+ for (i = 0; i < argc; i++) {
+ if (JS_IsException(r))
+ break;
+ r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));
+ }
+ return r;
+}
+
+static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)
+{
+ int i, c1, c2;
+ for (i = 0; i < len; i++) {
+ if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))
+ return c1 - c2;
+ }
+ return 0;
+}
+
+static int string_indexof_char(JSString *p, int c, int from)
+{
+ /* assuming 0 <= from <= p->len */
+ int i, len = p->len;
+ if (p->is_wide_char) {
+ for (i = from; i < len; i++) {
+ if (p->u.str16[i] == c)
+ return i;
+ }
+ } else {
+ if ((c & ~0xff) == 0) {
+ for (i = from; i < len; i++) {
+ if (p->u.str8[i] == (uint8_t)c)
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+static int string_indexof(JSString *p1, JSString *p2, int from)
+{
+ /* assuming 0 <= from <= p1->len */
+ int c, i, j, len1 = p1->len, len2 = p2->len;
+ if (len2 == 0)
+ return from;
+ for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {
+ j = string_indexof_char(p1, c, i);
+ if (j < 0 || j + len2 > len1)
+ break;
+ if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))
+ return j;
+ }
+ return -1;
+}
+
+static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
+{
+ if (!unicode || index >= p->len || !p->is_wide_char) {
+ index++;
+ } else {
+ int index32 = (int)index;
+ string_getc(p, &index32);
+ index = index32;
+ }
+ return index;
+}
+
+static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int lastIndexOf)
+{
+ JSValue str, v;
+ int i, len, v_len, pos, start, stop, ret, inc;
+ JSString *p;
+ JSString *p1;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ v = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(v))
+ goto fail;
+ p = JS_VALUE_GET_STRING(str);
+ p1 = JS_VALUE_GET_STRING(v);
+ len = p->len;
+ v_len = p1->len;
+ if (lastIndexOf) {
+ pos = len - v_len;
+ if (argc > 1) {
+ double d;
+ if (JS_ToFloat64(ctx, &d, argv[1]))
+ goto fail;
+ if (!isnan(d)) {
+ if (d <= 0)
+ pos = 0;
+ else if (d < pos)
+ pos = d;
+ }
+ }
+ start = pos;
+ stop = 0;
+ inc = -1;
+ } else {
+ pos = 0;
+ if (argc > 1) {
+ if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
+ goto fail;
+ }
+ start = pos;
+ stop = len - v_len;
+ inc = 1;
+ }
+ ret = -1;
+ if (len >= v_len && inc * (stop - start) >= 0) {
+ for (i = start;; i += inc) {
+ if (!string_cmp(p, p1, i, 0, v_len)) {
+ ret = i;
+ break;
+ }
+ if (i == stop)
+ break;
+ }
+ }
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, v);
+ return JS_NewInt32(ctx, ret);
+
+fail:
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, v);
+ return JS_EXCEPTION;
+}
+
+/* return < 0 if exception or TRUE/FALSE */
+static int js_is_regexp(JSContext *ctx, JSValueConst obj);
+
+static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue str, v = JS_UNDEFINED;
+ int i, len, v_len, pos, start, stop, ret;
+ JSString *p;
+ JSString *p1;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ ret = js_is_regexp(ctx, argv[0]);
+ if (ret) {
+ if (ret > 0)
+ JS_ThrowTypeError(ctx, "regex not supported");
+ goto fail;
+ }
+ v = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(v))
+ goto fail;
+ p = JS_VALUE_GET_STRING(str);
+ p1 = JS_VALUE_GET_STRING(v);
+ len = p->len;
+ v_len = p1->len;
+ pos = (magic == 2) ? len : 0;
+ if (argc > 1 && !JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
+ goto fail;
+ }
+ len -= v_len;
+ ret = 0;
+ if (magic == 0) {
+ start = pos;
+ stop = len;
+ } else {
+ if (magic == 1) {
+ if (pos > len)
+ goto done;
+ } else {
+ pos -= v_len;
+ }
+ start = stop = pos;
+ }
+ if (start >= 0 && start <= stop) {
+ for (i = start;; i++) {
+ if (!string_cmp(p, p1, i, 0, v_len)) {
+ ret = 1;
+ break;
+ }
+ if (i == stop)
+ break;
+ }
+ }
+ done:
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, v);
+ return JS_NewBool(ctx, ret);
+
+fail:
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, v);
+ return JS_EXCEPTION;
+}
+
+static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
+{
+ int ret;
+ JSValue flags;
+
+ ret = js_is_regexp(ctx, regexp);
+ if (ret < 0)
+ return -1;
+ if (ret) {
+ flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
+ if (JS_IsException(flags))
+ return -1;
+ if (JS_IsUndefined(flags) || JS_IsNull(flags)) {
+ JS_ThrowTypeError(ctx, "cannot convert to object");
+ return -1;
+ }
+ flags = JS_ToStringFree(ctx, flags);
+ if (JS_IsException(flags))
+ return -1;
+ ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
+ JS_FreeValue(ctx, flags);
+ if (ret < 0) {
+ JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int atom)
+{
+ // match(rx), search(rx), matchAll(rx)
+ // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll
+ JSValueConst O = this_val, regexp = argv[0], args[2];
+ JSValue matcher, S, rx, result, str;
+ int args_len;
+
+ if (JS_IsUndefined(O) || JS_IsNull(O))
+ return JS_ThrowTypeError(ctx, "cannot convert to object");
+
+ if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {
+ matcher = JS_GetProperty(ctx, regexp, atom);
+ if (JS_IsException(matcher))
+ return JS_EXCEPTION;
+ if (atom == JS_ATOM_Symbol_matchAll) {
+ if (check_regexp_g_flag(ctx, regexp) < 0) {
+ JS_FreeValue(ctx, matcher);
+ return JS_EXCEPTION;
+ }
+ }
+ if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {
+ return JS_CallFree(ctx, matcher, regexp, 1, &O);
+ }
+ }
+ S = JS_ToString(ctx, O);
+ if (JS_IsException(S))
+ return JS_EXCEPTION;
+ args_len = 1;
+ args[0] = regexp;
+ str = JS_UNDEFINED;
+ if (atom == JS_ATOM_Symbol_matchAll) {
+ str = JS_NewString(ctx, "g");
+ if (JS_IsException(str))
+ goto fail;
+ args[args_len++] = str;
+ }
+ rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
+ JS_FreeValue(ctx, str);
+ if (JS_IsException(rx)) {
+ fail:
+ JS_FreeValue(ctx, S);
+ return JS_EXCEPTION;
+ }
+ result = JS_InvokeFree(ctx, rx, atom, 1, &S);
+ JS_FreeValue(ctx, S);
+ return result;
+}
+
+static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // GetSubstitution(matched, str, position, captures, namedCaptures, rep)
+ JSValueConst matched, str, captures, namedCaptures, rep;
+ JSValue capture, name, s;
+ uint32_t position, len, matched_len, captures_len;
+ int i, j, j0, k, k1;
+ int c, c1;
+ StringBuffer b_s, *b = &b_s;
+ JSString *sp, *rp;
+
+ matched = argv[0];
+ str = argv[1];
+ captures = argv[3];
+ namedCaptures = argv[4];
+ rep = argv[5];
+
+ if (!JS_IsString(rep) || !JS_IsString(str))
+ return JS_ThrowTypeError(ctx, "not a string");
+
+ sp = JS_VALUE_GET_STRING(str);
+ rp = JS_VALUE_GET_STRING(rep);
+
+ string_buffer_init(ctx, b, 0);
+
+ captures_len = 0;
+ if (!JS_IsUndefined(captures)) {
+ if (js_get_length32(ctx, &captures_len, captures))
+ goto exception;
+ }
+ if (js_get_length32(ctx, &matched_len, matched))
+ goto exception;
+ if (JS_ToUint32(ctx, &position, argv[2]) < 0)
+ goto exception;
+
+ len = rp->len;
+ i = 0;
+ for(;;) {
+ j = string_indexof_char(rp, '$', i);
+ if (j < 0 || j + 1 >= len)
+ break;
+ string_buffer_concat(b, rp, i, j);
+ j0 = j++;
+ c = string_get(rp, j++);
+ if (c == '$') {
+ string_buffer_putc8(b, '$');
+ } else if (c == '&') {
+ if (string_buffer_concat_value(b, matched))
+ goto exception;
+ } else if (c == '`') {
+ string_buffer_concat(b, sp, 0, position);
+ } else if (c == '\'') {
+ string_buffer_concat(b, sp, position + matched_len, sp->len);
+ } else if (c >= '0' && c <= '9') {
+ k = c - '0';
+ if (j < len) {
+ c1 = string_get(rp, j);
+ if (c1 >= '0' && c1 <= '9') {
+ /* This behavior is specified in ES6 and refined in ECMA 2019 */
+ /* ECMA 2019 does not have the extra test, but
+ Test262 S15.5.4.11_A3_T1..3 require this behavior */
+ k1 = k * 10 + c1 - '0';
+ if (k1 >= 1 && k1 < captures_len) {
+ k = k1;
+ j++;
+ }
+ }
+ }
+ if (k >= 1 && k < captures_len) {
+ s = JS_GetPropertyInt64(ctx, captures, k);
+ if (JS_IsException(s))
+ goto exception;
+ if (!JS_IsUndefined(s)) {
+ if (string_buffer_concat_value_free(b, s))
+ goto exception;
+ }
+ } else {
+ goto norep;
+ }
+ } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {
+ k = string_indexof_char(rp, '>', j);
+ if (k < 0)
+ goto norep;
+ name = js_sub_string(ctx, rp, j, k);
+ if (JS_IsException(name))
+ goto exception;
+ capture = JS_GetPropertyValue(ctx, namedCaptures, name);
+ if (JS_IsException(capture))
+ goto exception;
+ if (!JS_IsUndefined(capture)) {
+ if (string_buffer_concat_value_free(b, capture))
+ goto exception;
+ }
+ j = k + 1;
+ } else {
+ norep:
+ string_buffer_concat(b, rp, j0, j);
+ }
+ i = j;
+ }
+ string_buffer_concat(b, rp, i, rp->len);
+ return string_buffer_end(b);
+exception:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int is_replaceAll)
+{
+ // replace(rx, rep)
+ JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
+ JSValueConst args[6];
+ JSValue str, search_str, replaceValue_str, repl_str;
+ JSString *sp, *searchp;
+ StringBuffer b_s, *b = &b_s;
+ int pos, functionalReplace, endOfLastMatch;
+ BOOL is_first;
+
+ if (JS_IsUndefined(O) || JS_IsNull(O))
+ return JS_ThrowTypeError(ctx, "cannot convert to object");
+
+ search_str = JS_UNDEFINED;
+ replaceValue_str = JS_UNDEFINED;
+ repl_str = JS_UNDEFINED;
+
+ if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {
+ JSValue replacer;
+ if (is_replaceAll) {
+ if (check_regexp_g_flag(ctx, searchValue) < 0)
+ return JS_EXCEPTION;
+ }
+ replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
+ if (JS_IsException(replacer))
+ return JS_EXCEPTION;
+ if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {
+ args[0] = O;
+ args[1] = replaceValue;
+ return JS_CallFree(ctx, replacer, searchValue, 2, args);
+ }
+ }
+ string_buffer_init(ctx, b, 0);
+
+ str = JS_ToString(ctx, O);
+ if (JS_IsException(str))
+ goto exception;
+ search_str = JS_ToString(ctx, searchValue);
+ if (JS_IsException(search_str))
+ goto exception;
+ functionalReplace = JS_IsFunction(ctx, replaceValue);
+ if (!functionalReplace) {
+ replaceValue_str = JS_ToString(ctx, replaceValue);
+ if (JS_IsException(replaceValue_str))
+ goto exception;
+ }
+
+ sp = JS_VALUE_GET_STRING(str);
+ searchp = JS_VALUE_GET_STRING(search_str);
+ endOfLastMatch = 0;
+ is_first = TRUE;
+ for(;;) {
+ if (unlikely(searchp->len == 0)) {
+ if (is_first)
+ pos = 0;
+ else if (endOfLastMatch >= sp->len)
+ pos = -1;
+ else
+ pos = endOfLastMatch + 1;
+ } else {
+ pos = string_indexof(sp, searchp, endOfLastMatch);
+ }
+ if (pos < 0) {
+ if (is_first) {
+ string_buffer_free(b);
+ JS_FreeValue(ctx, search_str);
+ JS_FreeValue(ctx, replaceValue_str);
+ return str;
+ } else {
+ break;
+ }
+ }
+ if (functionalReplace) {
+ args[0] = search_str;
+ args[1] = JS_NewInt32(ctx, pos);
+ args[2] = str;
+ repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));
+ } else {
+ args[0] = search_str;
+ args[1] = str;
+ args[2] = JS_NewInt32(ctx, pos);
+ args[3] = JS_UNDEFINED;
+ args[4] = JS_UNDEFINED;
+ args[5] = replaceValue_str;
+ repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
+ }
+ if (JS_IsException(repl_str))
+ goto exception;
+
+ string_buffer_concat(b, sp, endOfLastMatch, pos);
+ string_buffer_concat_value_free(b, repl_str);
+ endOfLastMatch = pos + searchp->len;
+ is_first = FALSE;
+ if (!is_replaceAll)
+ break;
+ }
+ string_buffer_concat(b, sp, endOfLastMatch, sp->len);
+ JS_FreeValue(ctx, search_str);
+ JS_FreeValue(ctx, replaceValue_str);
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+exception:
+ string_buffer_free(b);
+ JS_FreeValue(ctx, search_str);
+ JS_FreeValue(ctx, replaceValue_str);
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // split(sep, limit)
+ JSValueConst O = this_val, separator = argv[0], limit = argv[1];
+ JSValueConst args[2];
+ JSValue S, A, R, T;
+ uint32_t lim, lengthA;
+ int64_t p, q, s, r, e;
+ JSString *sp, *rp;
+
+ if (JS_IsUndefined(O) || JS_IsNull(O))
+ return JS_ThrowTypeError(ctx, "cannot convert to object");
+
+ S = JS_UNDEFINED;
+ A = JS_UNDEFINED;
+ R = JS_UNDEFINED;
+
+ if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {
+ JSValue splitter;
+ splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
+ if (JS_IsException(splitter))
+ return JS_EXCEPTION;
+ if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {
+ args[0] = O;
+ args[1] = limit;
+ return JS_CallFree(ctx, splitter, separator, 2, args);
+ }
+ }
+ S = JS_ToString(ctx, O);
+ if (JS_IsException(S))
+ goto exception;
+ A = JS_NewArray(ctx);
+ if (JS_IsException(A))
+ goto exception;
+ lengthA = 0;
+ if (JS_IsUndefined(limit)) {
+ lim = 0xffffffff;
+ } else {
+ if (JS_ToUint32(ctx, &lim, limit) < 0)
+ goto exception;
+ }
+ sp = JS_VALUE_GET_STRING(S);
+ s = sp->len;
+ R = JS_ToString(ctx, separator);
+ if (JS_IsException(R))
+ goto exception;
+ rp = JS_VALUE_GET_STRING(R);
+ r = rp->len;
+ p = 0;
+ if (lim == 0)
+ goto done;
+ if (JS_IsUndefined(separator))
+ goto add_tail;
+ if (s == 0) {
+ if (r != 0)
+ goto add_tail;
+ goto done;
+ }
+ q = p;
+ for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
+ e = string_indexof(sp, rp, q);
+ if (e < 0)
+ break;
+ T = js_sub_string(ctx, sp, p, e);
+ if (JS_IsException(T))
+ goto exception;
+ if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)
+ goto exception;
+ if (lengthA == lim)
+ goto done;
+ }
+add_tail:
+ T = js_sub_string(ctx, sp, p, s);
+ if (JS_IsException(T))
+ goto exception;
+ if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)
+ goto exception;
+done:
+ JS_FreeValue(ctx, S);
+ JS_FreeValue(ctx, R);
+ return A;
+
+exception:
+ JS_FreeValue(ctx, A);
+ JS_FreeValue(ctx, S);
+ JS_FreeValue(ctx, R);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str, ret;
+ int a, b, start, end;
+ JSString *p;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ b = p->len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ }
+ if (a < b) {
+ start = a;
+ end = b;
+ } else {
+ start = b;
+ end = a;
+ }
+ ret = js_sub_string(ctx, p, start, end);
+ JS_FreeValue(ctx, str);
+ return ret;
+}
+
+static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str, ret;
+ int a, len, n;
+ JSString *p;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ len = p->len;
+ if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ n = len - a;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ }
+ ret = js_sub_string(ctx, p, a, a + n);
+ JS_FreeValue(ctx, str);
+ return ret;
+}
+
+static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str, ret;
+ int len, start, end;
+ JSString *p;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ len = p->len;
+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ end = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ }
+ ret = js_sub_string(ctx, p, start, max_int(end, start));
+ JS_FreeValue(ctx, str);
+ return ret;
+}
+
+static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int padEnd)
+{
+ JSValue str, v = JS_UNDEFINED;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p, *p1 = NULL;
+ int n, len, c = ' ';
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ goto fail1;
+ if (JS_ToInt32Sat(ctx, &n, argv[0]))
+ goto fail2;
+ p = JS_VALUE_GET_STRING(str);
+ len = p->len;
+ if (len >= n)
+ return str;
+ if (argc > 1 && !JS_IsUndefined(argv[1])) {
+ v = JS_ToString(ctx, argv[1]);
+ if (JS_IsException(v))
+ goto fail2;
+ p1 = JS_VALUE_GET_STRING(v);
+ if (p1->len == 0) {
+ JS_FreeValue(ctx, v);
+ return str;
+ }
+ if (p1->len == 1) {
+ c = string_get(p1, 0);
+ p1 = NULL;
+ }
+ }
+ if (n > JS_STRING_LEN_MAX) {
+ JS_ThrowInternalError(ctx, "string too long");
+ goto fail2;
+ }
+ if (string_buffer_init(ctx, b, n))
+ goto fail3;
+ n -= len;
+ if (padEnd) {
+ if (string_buffer_concat(b, p, 0, len))
+ goto fail;
+ }
+ if (p1) {
+ while (n > 0) {
+ int chunk = min_int(n, p1->len);
+ if (string_buffer_concat(b, p1, 0, chunk))
+ goto fail;
+ n -= chunk;
+ }
+ } else {
+ if (string_buffer_fill(b, c, n))
+ goto fail;
+ }
+ if (!padEnd) {
+ if (string_buffer_concat(b, p, 0, len))
+ goto fail;
+ }
+ JS_FreeValue(ctx, v);
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+fail:
+ string_buffer_free(b);
+fail3:
+ JS_FreeValue(ctx, v);
+fail2:
+ JS_FreeValue(ctx, str);
+fail1:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int64_t val;
+ int n, len;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ goto fail;
+ if (JS_ToInt64Sat(ctx, &val, argv[0]))
+ goto fail;
+ if (val < 0 || val > 2147483647) {
+ JS_ThrowRangeError(ctx, "invalid repeat count");
+ goto fail;
+ }
+ n = val;
+ p = JS_VALUE_GET_STRING(str);
+ len = p->len;
+ if (len == 0 || n == 1)
+ return str;
+ if (val * len > JS_STRING_LEN_MAX) {
+ JS_ThrowInternalError(ctx, "string too long");
+ goto fail;
+ }
+ if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))
+ goto fail;
+ if (len == 1) {
+ string_buffer_fill(b, string_get(p, 0), n);
+ } else {
+ while (n-- > 0) {
+ string_buffer_concat(b, p, 0, len);
+ }
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+fail:
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue str, ret;
+ int a, b, len;
+ JSString *p;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ a = 0;
+ b = len = p->len;
+ if (magic & 1) {
+ while (a < len && lre_is_space(string_get(p, a)))
+ a++;
+ }
+ if (magic & 2) {
+ while (b > a && lre_is_space(string_get(p, b - 1)))
+ b--;
+ }
+ ret = js_sub_string(ctx, p, a, b);
+ JS_FreeValue(ctx, str);
+ return ret;
+}
+
+static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToQuotedString(ctx, this_val);
+}
+
+/* return 0 if before the first char */
+static int string_prevc(JSString *p, int *pidx)
+{
+ int idx, c, c1;
+
+ idx = *pidx;
+ if (idx <= 0)
+ return 0;
+ idx--;
+ if (p->is_wide_char) {
+ c = p->u.str16[idx];
+ if (c >= 0xdc00 && c < 0xe000 && idx > 0) {
+ c1 = p->u.str16[idx - 1];
+ if (c1 >= 0xd800 && c1 <= 0xdc00) {
+ c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;
+ idx--;
+ }
+ }
+ } else {
+ c = p->u.str8[idx];
+ }
+ *pidx = idx;
+ return c;
+}
+
+static BOOL test_final_sigma(JSString *p, int sigma_pos)
+{
+ int k, c1;
+
+ /* before C: skip case ignorable chars and check there is
+ a cased letter */
+ k = sigma_pos;
+ for(;;) {
+ c1 = string_prevc(p, &k);
+ if (!lre_is_case_ignorable(c1))
+ break;
+ }
+ if (!lre_is_cased(c1))
+ return FALSE;
+
+ /* after C: skip case ignorable chars and check there is
+ no cased letter */
+ k = sigma_pos + 1;
+ for(;;) {
+ if (k >= p->len)
+ return TRUE;
+ c1 = string_getc(p, &k);
+ if (!lre_is_case_ignorable(c1))
+ break;
+ }
+ return !lre_is_cased(c1);
+}
+
+static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue a, b;
+ int cmp;
+
+ a = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(a))
+ return JS_EXCEPTION;
+ b = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(b)) {
+ JS_FreeValue(ctx, a);
+ return JS_EXCEPTION;
+ }
+ cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
+ JS_FreeValue(ctx, a);
+ JS_FreeValue(ctx, b);
+ return JS_NewInt32(ctx, cmp);
+}
+
+static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int to_lower)
+{
+ JSValue val;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int i, c, j, l;
+ uint32_t res[LRE_CC_RES_LEN_MAX];
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+ if (p->len == 0)
+ return val;
+ if (string_buffer_init(ctx, b, p->len))
+ goto fail;
+ for(i = 0; i < p->len;) {
+ c = string_getc(p, &i);
+ if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {
+ res[0] = 0x3c2; /* final sigma */
+ l = 1;
+ } else {
+ l = lre_case_conv(res, c, to_lower);
+ }
+ for(j = 0; j < l; j++) {
+ if (string_buffer_putc(b, res[j]))
+ goto fail;
+ }
+ }
+ JS_FreeValue(ctx, val);
+ return string_buffer_end(b);
+ fail:
+ JS_FreeValue(ctx, val);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+#ifdef CONFIG_ALL_UNICODE
+
+/* return (-1, NULL) if exception, otherwise (len, buf) */
+static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)
+{
+ JSValue val;
+ JSString *p;
+ uint32_t *buf;
+ int i, j, len;
+
+ val = JS_ToString(ctx, val1);
+ if (JS_IsException(val))
+ return -1;
+ p = JS_VALUE_GET_STRING(val);
+ len = p->len;
+ /* UTF32 buffer length is len minus the number of correct surrogates pairs */
+ buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));
+ if (!buf) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ for(i = j = 0; i < len;)
+ buf[j++] = string_getc(p, &i);
+ JS_FreeValue(ctx, val);
+ *pbuf = buf;
+ return j;
+ fail:
+ *pbuf = NULL;
+ return -1;
+}
+
+static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
+{
+ int i;
+ StringBuffer b_s, *b = &b_s;
+ if (string_buffer_init(ctx, b, len))
+ return JS_EXCEPTION;
+ for(i = 0; i < len; i++) {
+ if (string_buffer_putc(b, buf[i]))
+ goto fail;
+ }
+ return string_buffer_end(b);
+ fail:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *form, *p;
+ size_t form_len;
+ int is_compat, buf_len, out_len;
+ UnicodeNormalizationEnum n_type;
+ JSValue val;
+ uint32_t *buf, *out_buf;
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ buf_len = JS_ToUTF32String(ctx, &buf, val);
+ JS_FreeValue(ctx, val);
+ if (buf_len < 0)
+ return JS_EXCEPTION;
+
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ n_type = UNICODE_NFC;
+ } else {
+ form = JS_ToCStringLen(ctx, &form_len, argv[0]);
+ if (!form)
+ goto fail1;
+ p = form;
+ if (p[0] != 'N' || p[1] != 'F')
+ goto bad_form;
+ p += 2;
+ is_compat = FALSE;
+ if (*p == 'K') {
+ is_compat = TRUE;
+ p++;
+ }
+ if (*p == 'C' || *p == 'D') {
+ n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');
+ if ((p + 1 - form) != form_len)
+ goto bad_form;
+ } else {
+ bad_form:
+ JS_FreeCString(ctx, form);
+ JS_ThrowRangeError(ctx, "bad normalization form");
+ fail1:
+ js_free(ctx, buf);
+ return JS_EXCEPTION;
+ }
+ JS_FreeCString(ctx, form);
+ }
+
+ out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
+ ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
+ js_free(ctx, buf);
+ if (out_len < 0)
+ return JS_EXCEPTION;
+ val = JS_NewUTF32String(ctx, out_buf, out_len);
+ js_free(ctx, out_buf);
+ return val;
+}
+#endif /* CONFIG_ALL_UNICODE */
+
+/* also used for String.prototype.valueOf */
+static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisStringValue(ctx, this_val);
+}
+
+#if 0
+static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToStringCheckObject(ctx, argv[0]);
+}
+
+static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToString(ctx, argv[0]);
+}
+
+static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst
+ this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ int idx;
+ BOOL is_unicode;
+ JSString *p;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+ if (JS_ToInt32Sat(ctx, &idx, argv[1])) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ is_unicode = JS_ToBool(ctx, argv[2]);
+ p = JS_VALUE_GET_STRING(str);
+ if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {
+ idx++;
+ } else {
+ string_getc(p, &idx);
+ }
+ JS_FreeValue(ctx, str);
+ return JS_NewInt32(ctx, idx);
+}
+#endif
+
+/* String Iterator */
+
+static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSArrayIteratorData *it;
+ uint32_t idx, c, start;
+ JSString *p;
+
+ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);
+ if (!it) {
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+ }
+ if (JS_IsUndefined(it->obj))
+ goto done;
+ p = JS_VALUE_GET_STRING(it->obj);
+ idx = it->idx;
+ if (idx >= p->len) {
+ JS_FreeValue(ctx, it->obj);
+ it->obj = JS_UNDEFINED;
+ done:
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ }
+
+ start = idx;
+ c = string_getc(p, (int *)&idx);
+ it->idx = idx;
+ *pdone = FALSE;
+ if (c <= 0xffff) {
+ return js_new_string_char(ctx, c);
+ } else {
+ return js_new_string16(ctx, p->u.str16 + start, 2);
+ }
+}
+
+/* ES6 Annex B 2.3.2 etc. */
+enum {
+ magic_string_anchor,
+ magic_string_big,
+ magic_string_blink,
+ magic_string_bold,
+ magic_string_fixed,
+ magic_string_fontcolor,
+ magic_string_fontsize,
+ magic_string_italics,
+ magic_string_link,
+ magic_string_small,
+ magic_string_strike,
+ magic_string_sub,
+ magic_string_sup,
+};
+
+static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue str;
+ const JSString *p;
+ StringBuffer b_s, *b = &b_s;
+ static struct { const char *tag, *attr; } const defs[] = {
+ { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },
+ { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },
+ { "a", "href" }, { "small", NULL }, { "strike", NULL },
+ { "sub", NULL }, { "sup", NULL },
+ };
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return JS_EXCEPTION;
+ string_buffer_init(ctx, b, 7);
+ string_buffer_putc8(b, '<');
+ string_buffer_puts8(b, defs[magic].tag);
+ if (defs[magic].attr) {
+ // r += " " + attr + "=\"" + value + "\"";
+ JSValue value;
+ int i;
+
+ string_buffer_putc8(b, ' ');
+ string_buffer_puts8(b, defs[magic].attr);
+ string_buffer_puts8(b, "=\"");
+ value = JS_ToStringCheckObject(ctx, argv[0]);
+ if (JS_IsException(value)) {
+ JS_FreeValue(ctx, str);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+ }
+ p = JS_VALUE_GET_STRING(value);
+ for (i = 0; i < p->len; i++) {
+ int c = string_get(p, i);
+ if (c == '"') {
+ string_buffer_puts8(b, "&quot;");
+ } else {
+ string_buffer_putc16(b, c);
+ }
+ }
+ JS_FreeValue(ctx, value);
+ string_buffer_putc8(b, '\"');
+ }
+ // return r + ">" + str + "</" + tag + ">";
+ string_buffer_putc8(b, '>');
+ string_buffer_concat_value_free(b, str);
+ string_buffer_puts8(b, "</");
+ string_buffer_puts8(b, defs[magic].tag);
+ string_buffer_putc8(b, '>');
+ return string_buffer_end(b);
+}
+
+static const JSCFunctionListEntry js_string_funcs[] = {
+ JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),
+ JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),
+ JS_CFUNC_DEF("raw", 1, js_string_raw ),
+ //JS_CFUNC_DEF("__toString", 1, js_string___toString ),
+ //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),
+ //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),
+ //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),
+ //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),
+};
+
+static const JSCFunctionListEntry js_string_proto_funcs[] = {
+ JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
+ JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),
+ JS_CFUNC_DEF("charAt", 1, js_string_charAt ),
+ JS_CFUNC_DEF("concat", 1, js_string_concat ),
+ JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
+ JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
+ JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
+ JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
+ JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),
+ JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),
+ JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),
+ JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),
+ JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),
+ JS_CFUNC_DEF("split", 2, js_string_split ),
+ JS_CFUNC_DEF("substring", 2, js_string_substring ),
+ JS_CFUNC_DEF("substr", 2, js_string_substr ),
+ JS_CFUNC_DEF("slice", 2, js_string_slice ),
+ JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
+ JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
+ JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
+ JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
+ JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
+ JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
+ JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
+ JS_ALIAS_DEF("trimRight", "trimEnd" ),
+ JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
+ JS_ALIAS_DEF("trimLeft", "trimStart" ),
+ JS_CFUNC_DEF("toString", 0, js_string_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
+ JS_CFUNC_DEF("__quote", 1, js_string___quote ),
+ JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
+ JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
+ JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
+ JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
+ /* ES6 Annex B 2.3.2 etc. */
+ JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
+ JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
+ JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
+ JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
+ JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
+ JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
+ JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
+ JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
+ JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
+ JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
+ JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
+ JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
+ JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
+};
+
+static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+#ifdef CONFIG_ALL_UNICODE
+static const JSCFunctionListEntry js_string_proto_normalize[] = {
+ JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
+};
+#endif
+
+void JS_AddIntrinsicStringNormalize(JSContext *ctx)
+{
+#ifdef CONFIG_ALL_UNICODE
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
+ countof(js_string_proto_normalize));
+#endif
+}
+
+/* Math */
+
+/* precondition: a and b are not NaN */
+static double js_fmin(double a, double b)
+{
+ if (a == 0 && b == 0) {
+ JSFloat64Union a1, b1;
+ a1.d = a;
+ b1.d = b;
+ a1.u64 |= b1.u64;
+ return a1.d;
+ } else {
+ return fmin(a, b);
+ }
+}
+
+/* precondition: a and b are not NaN */
+static double js_fmax(double a, double b)
+{
+ if (a == 0 && b == 0) {
+ JSFloat64Union a1, b1;
+ a1.d = a;
+ b1.d = b;
+ a1.u64 &= b1.u64;
+ return a1.d;
+ } else {
+ return fmax(a, b);
+ }
+}
+
+static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ BOOL is_max = magic;
+ double r, a;
+ int i;
+ uint32_t tag;
+
+ if (unlikely(argc == 0)) {
+ return JS_NewFloat64Impl(ctx, is_max ? INFINITY : -INFINITY);
+ }
+
+ tag = JS_VALUE_GET_TAG(argv[0]);
+ if (tag == JS_TAG_INT) {
+ int a1, r1 = JS_VALUE_GET_INT(argv[0]);
+ for(i = 1; i < argc; i++) {
+ tag = JS_VALUE_GET_TAG(argv[i]);
+ if (tag != JS_TAG_INT) {
+ r = r1;
+ goto generic_case;
+ }
+ a1 = JS_VALUE_GET_INT(argv[i]);
+ if (is_max)
+ r1 = max_int(r1, a1);
+ else
+ r1 = min_int(r1, a1);
+
+ }
+ return JS_NewInt32(ctx, r1);
+ } else {
+ if (JS_ToFloat64(ctx, &r, argv[0]))
+ return JS_EXCEPTION;
+ i = 1;
+ generic_case:
+ while (i < argc) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isnan(r)) {
+ if (isnan(a)) {
+ r = a;
+ } else {
+ if (is_max)
+ r = js_fmax(r, a);
+ else
+ r = js_fmin(r, a);
+ }
+ }
+ i++;
+ }
+ return JS_NewFloat64(ctx, r);
+ }
+}
+
+static double js_math_sign(double a)
+{
+ if (isnan(a) || a == 0.0)
+ return a;
+ if (a < 0)
+ return -1;
+ else
+ return 1;
+}
+
+static double js_math_round(double a)
+{
+ JSFloat64Union u;
+ uint64_t frac_mask, one;
+ unsigned int e, s;
+
+ u.d = a;
+ e = (u.u64 >> 52) & 0x7ff;
+ if (e < 1023) {
+ /* abs(a) < 1 */
+ if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
+ /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
+ u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
+ } else {
+ /* return +/-0.0 */
+ u.u64 &= (uint64_t)1 << 63;
+ }
+ } else if (e < (1023 + 52)) {
+ s = u.u64 >> 63;
+ one = (uint64_t)1 << (52 - (e - 1023));
+ frac_mask = one - 1;
+ u.u64 += (one >> 1) - s;
+ u.u64 &= ~frac_mask; /* truncate to an integer */
+ }
+ /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
+ return u.d;
+}
+
+static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double r, a;
+ int i;
+
+ r = 0;
+ if (argc > 0) {
+ if (JS_ToFloat64(ctx, &r, argv[0]))
+ return JS_EXCEPTION;
+ if (argc == 1) {
+ r = fabs(r);
+ } else {
+ /* use the built-in function to minimize precision loss */
+ for (i = 1; i < argc; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ r = hypot(r, a);
+ }
+ }
+ }
+ return JS_NewFloat64(ctx, r);
+}
+
+static double js_math_fround(double a)
+{
+ return (float)a;
+}
+
+static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int a, b;
+
+ if (JS_ToInt32(ctx, &a, argv[0]))
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &b, argv[1]))
+ return JS_EXCEPTION;
+ /* purposely ignoring overflow */
+ return JS_NewInt32(ctx, a * b);
+}
+
+static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint32_t a, r;
+
+ if (JS_ToUint32(ctx, &a, argv[0]))
+ return JS_EXCEPTION;
+ if (a == 0)
+ r = 32;
+ else
+ r = clz32(a);
+ return JS_NewInt32(ctx, r);
+}
+
+/* xorshift* random number generator by Marsaglia */
+static uint64_t xorshift64star(uint64_t *pstate)
+{
+ uint64_t x;
+ x = *pstate;
+ x ^= x >> 12;
+ x ^= x << 25;
+ x ^= x >> 27;
+ *pstate = x;
+ return x * 0x2545F4914F6CDD1D;
+}
+
+static void js_random_init(JSContext *ctx)
+{
+#ifdef _MSC_VER
+ ctx->random_state = time(NULL);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
+#endif
+ /* the state must be non zero */
+ if (ctx->random_state == 0)
+ ctx->random_state = 1;
+}
+
+static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSFloat64Union u;
+ uint64_t v;
+
+ v = xorshift64star(&ctx->random_state);
+ /* 1.0 <= u.d < 2 */
+ u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
+ return JS_NewFloat64Impl(ctx, u.d - 1.0);
+}
+
+// MSVC inexplicably refuses to initialize the array below with
+// these functions, so use wrappers.
+static double floorWrapper(double x) { return floor(x); }
+static double ceilWrapper(double x) { return ceil(x); }
+static double log2Wrapper(double x) { return log2(x); }
+
+static const JSCFunctionListEntry js_math_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
+ JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
+ JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
+ JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floorWrapper ),
+ JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceilWrapper ),
+ JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
+ JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
+
+ JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),
+ JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),
+ JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),
+ JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),
+ JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),
+ JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),
+ JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),
+ JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),
+ JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),
+ JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),
+ /* ES6 */
+ JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),
+ JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
+ JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),
+ JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),
+ JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),
+ JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),
+ JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),
+ JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
+ JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
+ JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
+ JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2Wrapper ),
+ JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
+ JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
+ JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
+ JS_CFUNC_DEF("random", 0, js_math_random ),
+ JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
+ JS_CFUNC_DEF("imul", 2, js_math_imul ),
+ JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
+ JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
+ JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
+ JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
+ JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
+ JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
+ JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
+ JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
+ JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
+};
+
+static const JSCFunctionListEntry js_math_obj[] = {
+ JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+/* Date */
+
+#if 0
+/* OS dependent: return the UTC time in ms since 1970. */
+static JSValue js___date_now(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int64_t d;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
+ return JS_NewInt64(ctx, d);
+}
+#endif
+
+/* OS dependent: return the UTC time in microseconds since 1970. */
+static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int64_t d;
+#ifdef _MSC_VER
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, (FILETIME *) &d);
+ d /= 10;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+#endif
+ return JS_NewInt64(ctx, d);
+}
+
+/* OS dependent. d = argv[0] is in ms from 1970. Return the difference
+ between UTC time and local time 'd' in minutes */
+static int getTimezoneOffset(int64_t time) {
+#if defined(_WIN32)
+ /* XXX: TODO */
+ return 0;
+#else
+ time_t ti;
+ struct tm tm;
+
+ time /= 1000; /* convert to seconds */
+ if (sizeof(time_t) == 4) {
+ /* on 32-bit systems, we need to clamp the time value to the
+ range of `time_t`. This is better than truncating values to
+ 32 bits and hopefully provides the same result as 64-bit
+ implementation of localtime_r.
+ */
+ if ((time_t)-1 < 0) {
+ if (time < INT32_MIN) {
+ time = INT32_MIN;
+ } else if (time > INT32_MAX) {
+ time = INT32_MAX;
+ }
+ } else {
+ if (time < 0) {
+ time = 0;
+ } else if (time > UINT32_MAX) {
+ time = UINT32_MAX;
+ }
+ }
+ }
+ ti = time;
+ localtime_r(&ti, &tm);
+ return -tm.tm_gmtoff / 60;
+#endif
+}
+
+#if 0
+static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double dd;
+
+ if (JS_ToFloat64(ctx, &dd, argv[0]))
+ return JS_EXCEPTION;
+ if (isnan(dd))
+ return __JS_NewFloat64(ctx, dd);
+ else
+ return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));
+}
+
+static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,
+ JSValueConst def_proto)
+{
+ JSValue proto;
+ proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
+ if (JS_IsException(proto))
+ return proto;
+ if (!JS_IsObject(proto)) {
+ JS_FreeValue(ctx, proto);
+ proto = JS_DupValue(ctx, def_proto);
+ }
+ return proto;
+}
+
+/* create a new date object */
+static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, proto;
+ proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);
+ if (JS_IsException(proto))
+ return proto;
+ obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);
+ JS_FreeValue(ctx, proto);
+ if (!JS_IsException(obj))
+ JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));
+ return obj;
+}
+#endif
+
+/* RegExp */
+
+static void js_regexp_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSRegExp *re = &p->u.regexp;
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));
+}
+
+/* create a string containing the RegExp bytecode */
+static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
+ JSValueConst flags)
+{
+ const char *str;
+ int re_flags, mask;
+ uint8_t *re_bytecode_buf;
+ size_t i, len;
+ int re_bytecode_len;
+ JSValue ret;
+ char error_msg[64];
+
+ re_flags = 0;
+ if (!JS_IsUndefined(flags)) {
+ str = JS_ToCStringLen(ctx, &len, flags);
+ if (!str)
+ return JS_EXCEPTION;
+ /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
+ for (i = 0; i < len; i++) {
+ switch(str[i]) {
+ case 'g':
+ mask = LRE_FLAG_GLOBAL;
+ break;
+ case 'i':
+ mask = LRE_FLAG_IGNORECASE;
+ break;
+ case 'm':
+ mask = LRE_FLAG_MULTILINE;
+ break;
+ case 's':
+ mask = LRE_FLAG_DOTALL;
+ break;
+ case 'u':
+ mask = LRE_FLAG_UTF16;
+ break;
+ case 'y':
+ mask = LRE_FLAG_STICKY;
+ break;
+ default:
+ goto bad_flags;
+ }
+ if ((re_flags & mask) != 0) {
+ bad_flags:
+ JS_FreeCString(ctx, str);
+ return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");
+ }
+ re_flags |= mask;
+ }
+ JS_FreeCString(ctx, str);
+ }
+
+ str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16));
+ if (!str)
+ return JS_EXCEPTION;
+ re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,
+ sizeof(error_msg), str, len, re_flags, ctx);
+ JS_FreeCString(ctx, str);
+ if (!re_bytecode_buf) {
+ JS_ThrowSyntaxError(ctx, "%s", error_msg);
+ return JS_EXCEPTION;
+ }
+
+ ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);
+ js_free(ctx, re_bytecode_buf);
+ return ret;
+}
+
+/* create a RegExp object from a string containing the RegExp bytecode
+ and the source pattern */
+static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
+ JSValue pattern, JSValue bc)
+{
+ JSValue obj;
+ JSObject *p;
+ JSRegExp *re;
+
+ /* sanity check */
+ if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
+ JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {
+ JS_ThrowTypeError(ctx, "string expected");
+ fail:
+ JS_FreeValue(ctx, bc);
+ JS_FreeValue(ctx, pattern);
+ return JS_EXCEPTION;
+ }
+
+ obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);
+ if (JS_IsException(obj))
+ goto fail;
+ p = JS_VALUE_GET_OBJ(obj);
+ re = &p->u.regexp;
+ re->pattern = JS_VALUE_GET_STRING(pattern);
+ re->bytecode = JS_VALUE_GET_STRING(bc);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),
+ JS_PROP_WRITABLE);
+ return obj;
+}
+
+static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)
+{
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id == JS_CLASS_REGEXP)
+ return &p->u.regexp;
+ }
+ if (throw_error) {
+ JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
+ }
+ return NULL;
+}
+
+/* return < 0 if exception or TRUE/FALSE */
+static int js_is_regexp(JSContext *ctx, JSValueConst obj)
+{
+ JSValue m;
+
+ if (!JS_IsObject(obj))
+ return FALSE;
+ m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);
+ if (JS_IsException(m))
+ return -1;
+ if (!JS_IsUndefined(m))
+ return JS_ToBoolFree(ctx, m);
+ return js_get_regexp(ctx, obj, FALSE) != NULL;
+}
+
+static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue pattern, flags, bc, val;
+ JSValueConst pat, flags1;
+ JSRegExp *re;
+ int pat_is_regexp;
+
+ pat = argv[0];
+ flags1 = argv[1];
+ pat_is_regexp = js_is_regexp(ctx, pat);
+ if (pat_is_regexp < 0)
+ return JS_EXCEPTION;
+ if (JS_IsUndefined(new_target)) {
+ /* called as a function */
+ new_target = JS_GetActiveFunction(ctx);
+ if (pat_is_regexp && JS_IsUndefined(flags1)) {
+ JSValue ctor;
+ BOOL res;
+ ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);
+ if (JS_IsException(ctor))
+ return ctor;
+ res = js_same_value(ctx, ctor, new_target);
+ JS_FreeValue(ctx, ctor);
+ if (res)
+ return JS_DupValue(ctx, pat);
+ }
+ }
+ re = js_get_regexp(ctx, pat, FALSE);
+ if (re) {
+ pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
+ if (JS_IsUndefined(flags1)) {
+ bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
+ goto no_compilation;
+ } else {
+ flags = JS_ToString(ctx, flags1);
+ if (JS_IsException(flags))
+ goto fail;
+ }
+ } else {
+ flags = JS_UNDEFINED;
+ if (pat_is_regexp) {
+ pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);
+ if (JS_IsException(pattern))
+ goto fail;
+ if (JS_IsUndefined(flags1)) {
+ flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);
+ if (JS_IsException(flags))
+ goto fail;
+ } else {
+ flags = JS_DupValue(ctx, flags1);
+ }
+ } else {
+ pattern = JS_DupValue(ctx, pat);
+ flags = JS_DupValue(ctx, flags1);
+ }
+ if (JS_IsUndefined(pattern)) {
+ pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ } else {
+ val = pattern;
+ pattern = JS_ToString(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(pattern))
+ goto fail;
+ }
+ }
+ bc = js_compile_regexp(ctx, pattern, flags);
+ if (JS_IsException(bc))
+ goto fail;
+ JS_FreeValue(ctx, flags);
+ no_compilation:
+ return js_regexp_constructor_internal(ctx, new_target, pattern, bc);
+ fail:
+ JS_FreeValue(ctx, pattern);
+ JS_FreeValue(ctx, flags);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSRegExp *re1, *re;
+ JSValueConst pattern1, flags1;
+ JSValue bc, pattern;
+
+ re = js_get_regexp(ctx, this_val, TRUE);
+ if (!re)
+ return JS_EXCEPTION;
+ pattern1 = argv[0];
+ flags1 = argv[1];
+ re1 = js_get_regexp(ctx, pattern1, FALSE);
+ if (re1) {
+ if (!JS_IsUndefined(flags1))
+ return JS_ThrowTypeError(ctx, "flags must be undefined");
+ pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));
+ bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));
+ } else {
+ bc = JS_UNDEFINED;
+ if (JS_IsUndefined(pattern1))
+ pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ else
+ pattern = JS_ToString(ctx, pattern1);
+ if (JS_IsException(pattern))
+ goto fail;
+ bc = js_compile_regexp(ctx, pattern, flags1);
+ if (JS_IsException(bc))
+ goto fail;
+ }
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
+ re->pattern = JS_VALUE_GET_STRING(pattern);
+ re->bytecode = JS_VALUE_GET_STRING(bc);
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, 0)) < 0)
+ return JS_EXCEPTION;
+ return JS_DupValue(ctx, this_val);
+ fail:
+ JS_FreeValue(ctx, pattern);
+ JS_FreeValue(ctx, bc);
+ return JS_EXCEPTION;
+}
+
+#if 0
+static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)
+{
+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
+ if (!re)
+ return JS_EXCEPTION;
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
+}
+
+static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)
+{
+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
+ int flags;
+
+ if (!re)
+ return JS_EXCEPTION;
+ flags = lre_get_flags(re->bytecode->u.str8);
+ return JS_NewInt32(ctx, flags);
+}
+#endif
+
+static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)
+{
+ JSRegExp *re;
+ JSString *p;
+ StringBuffer b_s, *b = &b_s;
+ int i, n, c, c2, bra;
+
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
+ goto empty_regex;
+
+ re = js_get_regexp(ctx, this_val, TRUE);
+ if (!re)
+ return JS_EXCEPTION;
+
+ p = re->pattern;
+
+ if (p->len == 0) {
+ empty_regex:
+ return JS_NewString(ctx, "(?:)");
+ }
+ string_buffer_init2(ctx, b, p->len, p->is_wide_char);
+
+ /* Escape '/' and newline sequences as needed */
+ bra = 0;
+ for (i = 0, n = p->len; i < n;) {
+ c2 = -1;
+ switch (c = string_get(p, i++)) {
+ case '\\':
+ if (i < n)
+ c2 = string_get(p, i++);
+ break;
+ case ']':
+ bra = 0;
+ break;
+ case '[':
+ if (!bra) {
+ if (i < n && string_get(p, i) == ']')
+ c2 = string_get(p, i++);
+ bra = 1;
+ }
+ break;
+ case '\n':
+ c = '\\';
+ c2 = 'n';
+ break;
+ case '\r':
+ c = '\\';
+ c2 = 'r';
+ break;
+ case '/':
+ if (!bra) {
+ c = '\\';
+ c2 = '/';
+ }
+ break;
+ }
+ string_buffer_putc16(b, c);
+ if (c2 >= 0)
+ string_buffer_putc16(b, c2);
+ }
+ return string_buffer_end(b);
+}
+
+static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)
+{
+ JSRegExp *re;
+ int flags;
+
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ re = js_get_regexp(ctx, this_val, FALSE);
+ if (!re) {
+ if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
+ return JS_UNDEFINED;
+ else
+ return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
+ }
+
+ flags = lre_get_flags(re->bytecode->u.str8);
+ return JS_NewBool(ctx, (flags & mask) != 0);
+}
+
+static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
+{
+ char str[8], *p = str;
+ int res;
+
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'g';
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'i';
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'm';
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 's';
+ res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'u';
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'y';
+ return JS_NewStringLen(ctx, str, p - str);
+
+exception:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue pattern, flags;
+ StringBuffer b_s, *b = &b_s;
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ string_buffer_init(ctx, b, 0);
+ string_buffer_putc8(b, '/');
+ pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);
+ if (string_buffer_concat_value_free(b, pattern))
+ goto fail;
+ string_buffer_putc8(b, '/');
+ flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);
+ if (string_buffer_concat_value_free(b, flags))
+ goto fail;
+ return string_buffer_end(b);
+
+fail:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
+{
+ JSContext *ctx = opaque;
+ return js_check_stack_overflow(ctx->rt, alloca_size);
+}
+
+void *lre_realloc(void *opaque, void *ptr, size_t size)
+{
+ JSContext *ctx = opaque;
+ /* No JS exception is raised here */
+ return js_realloc_rt(ctx->rt, ptr, size);
+}
+
+static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
+ JSString *str;
+ JSValue str_val, obj, val, groups = JS_UNDEFINED;
+ uint8_t *re_bytecode;
+ int ret;
+ uint8_t **capture, *str_buf;
+ int capture_count, shift, i, re_flags;
+ int64_t last_index;
+ const char *group_name_ptr;
+
+ if (!re)
+ return JS_EXCEPTION;
+ str_val = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str_val))
+ return str_val;
+ val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
+ if (JS_IsException(val) ||
+ JS_ToLengthFree(ctx, &last_index, val)) {
+ JS_FreeValue(ctx, str_val);
+ return JS_EXCEPTION;
+ }
+ re_bytecode = re->bytecode->u.str8;
+ re_flags = lre_get_flags(re_bytecode);
+ if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
+ last_index = 0;
+ }
+ str = JS_VALUE_GET_STRING(str_val);
+ capture_count = lre_get_capture_count(re_bytecode);
+ capture = NULL;
+ if (capture_count > 0) {
+ capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
+ if (!capture) {
+ JS_FreeValue(ctx, str_val);
+ return JS_EXCEPTION;
+ }
+ }
+ shift = str->is_wide_char;
+ str_buf = str->u.str8;
+ if (last_index > str->len) {
+ ret = 2;
+ } else {
+ ret = lre_exec(capture, re_bytecode,
+ str_buf, last_index, str->len,
+ shift, ctx);
+ }
+ obj = JS_NULL;
+ if (ret != 1) {
+ if (ret >= 0) {
+ if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, 0)) < 0)
+ goto fail;
+ }
+ } else {
+ JS_ThrowInternalError(ctx, "out of memory in regexp execution");
+ goto fail;
+ }
+ JS_FreeValue(ctx, str_val);
+ } else {
+ int prop_flags;
+ if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)
+ goto fail;
+ }
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj))
+ goto fail;
+ prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
+ group_name_ptr = lre_get_groupnames(re_bytecode);
+ if (group_name_ptr) {
+ groups = JS_NewObjectProto(ctx, JS_NULL);
+ if (JS_IsException(groups))
+ goto fail;
+ }
+
+ for(i = 0; i < capture_count; i++) {
+ int start, end;
+ JSValue val;
+ if (capture[2 * i] == NULL ||
+ capture[2 * i + 1] == NULL) {
+ val = JS_UNDEFINED;
+ } else {
+ start = (capture[2 * i] - str_buf) >> shift;
+ end = (capture[2 * i + 1] - str_buf) >> shift;
+ val = js_sub_string(ctx, str, start, end);
+ if (JS_IsException(val))
+ goto fail;
+ }
+ if (group_name_ptr && i > 0) {
+ if (*group_name_ptr) {
+ if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,
+ JS_DupValue(ctx, val),
+ prop_flags) < 0) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ }
+ group_name_ptr += strlen(group_name_ptr) + 1;
+ }
+ if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)
+ goto fail;
+ }
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,
+ groups, prop_flags) < 0)
+ goto fail;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,
+ JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)
+ goto fail;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)
+ goto fail1;
+ }
+ js_free(ctx, capture);
+ return obj;
+fail:
+ JS_FreeValue(ctx, groups);
+ JS_FreeValue(ctx, str_val);
+fail1:
+ JS_FreeValue(ctx, obj);
+ js_free(ctx, capture);
+ return JS_EXCEPTION;
+}
+
+/* delete portions of a string that match a given regex */
+static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)
+{
+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
+ JSString *str;
+ JSValue str_val, val;
+ uint8_t *re_bytecode;
+ int ret;
+ uint8_t **capture, *str_buf;
+ int capture_count, shift, re_flags;
+ int next_src_pos, start, end;
+ int64_t last_index;
+ StringBuffer b_s, *b = &b_s;
+
+ if (!re)
+ return JS_EXCEPTION;
+
+ string_buffer_init(ctx, b, 0);
+
+ capture = NULL;
+ str_val = JS_ToString(ctx, arg);
+ if (JS_IsException(str_val))
+ goto fail;
+ str = JS_VALUE_GET_STRING(str_val);
+ re_bytecode = re->bytecode->u.str8;
+ re_flags = lre_get_flags(re_bytecode);
+ if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
+ last_index = 0;
+ } else {
+ val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
+ if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
+ goto fail;
+ }
+ capture_count = lre_get_capture_count(re_bytecode);
+ if (capture_count > 0) {
+ capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
+ if (!capture)
+ goto fail;
+ }
+ shift = str->is_wide_char;
+ str_buf = str->u.str8;
+ next_src_pos = 0;
+ for (;;) {
+ if (last_index > str->len)
+ break;
+
+ ret = lre_exec(capture, re_bytecode,
+ str_buf, last_index, str->len, shift, ctx);
+ if (ret != 1) {
+ if (ret >= 0) {
+ if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, 0)) < 0)
+ goto fail;
+ }
+ } else {
+ JS_ThrowInternalError(ctx, "out of memory in regexp execution");
+ goto fail;
+ }
+ break;
+ }
+ start = (capture[0] - str_buf) >> shift;
+ end = (capture[1] - str_buf) >> shift;
+ last_index = end;
+ if (next_src_pos < start) {
+ if (string_buffer_concat(b, str, next_src_pos, start))
+ goto fail;
+ }
+ next_src_pos = end;
+ if (!(re_flags & LRE_FLAG_GLOBAL)) {
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, end)) < 0)
+ goto fail;
+ break;
+ }
+ if (end == start) {
+ if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) {
+ end++;
+ } else {
+ string_getc(str, &end);
+ }
+ }
+ last_index = end;
+ }
+ if (string_buffer_concat(b, str, next_src_pos, str->len))
+ goto fail;
+ JS_FreeValue(ctx, str_val);
+ js_free(ctx, capture);
+ return string_buffer_end(b);
+fail:
+ JS_FreeValue(ctx, str_val);
+ js_free(ctx, capture);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
+{
+ JSValue method, ret;
+
+ method = JS_GetProperty(ctx, r, JS_ATOM_exec);
+ if (JS_IsException(method))
+ return method;
+ if (JS_IsFunction(ctx, method)) {
+ ret = JS_CallFree(ctx, method, r, 1, &s);
+ if (JS_IsException(ret))
+ return ret;
+ if (!JS_IsObject(ret) && !JS_IsNull(ret)) {
+ JS_FreeValue(ctx, ret);
+ return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");
+ }
+ return ret;
+ }
+ JS_FreeValue(ctx, method);
+ return js_regexp_exec(ctx, r, 1, &s);
+}
+
+#if 0
+static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_RegExpExec(ctx, argv[0], argv[1]);
+}
+static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_RegExpDelete(ctx, argv[0], argv[1]);
+}
+#endif
+
+static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ BOOL ret;
+
+ val = JS_RegExpExec(ctx, this_val, argv[0]);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ ret = !JS_IsNull(val);
+ JS_FreeValue(ctx, val);
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // [Symbol.match](str)
+ JSValueConst rx = this_val;
+ JSValue A, S, result, matchStr;
+ int global, n, fullUnicode, isEmpty;
+ JSString *p;
+
+ if (!JS_IsObject(rx))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ A = JS_UNDEFINED;
+ result = JS_UNDEFINED;
+ matchStr = JS_UNDEFINED;
+ S = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(S))
+ goto exception;
+
+ global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
+ if (global < 0)
+ goto exception;
+
+ if (!global) {
+ A = JS_RegExpExec(ctx, rx, S);
+ } else {
+ fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
+ if (fullUnicode < 0)
+ goto exception;
+
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
+ goto exception;
+ A = JS_NewArray(ctx);
+ if (JS_IsException(A))
+ goto exception;
+ n = 0;
+ for(;;) {
+ JS_FreeValue(ctx, result);
+ result = JS_RegExpExec(ctx, rx, S);
+ if (JS_IsException(result))
+ goto exception;
+ if (JS_IsNull(result))
+ break;
+ matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
+ if (JS_IsException(matchStr))
+ goto exception;
+ isEmpty = JS_IsEmptyString(matchStr);
+ if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)
+ goto exception;
+ if (isEmpty) {
+ int64_t thisIndex, nextIndex;
+ if (JS_ToLengthFree(ctx, &thisIndex,
+ JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
+ goto exception;
+ p = JS_VALUE_GET_STRING(S);
+ nextIndex = string_advance_index(p, thisIndex, fullUnicode);
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
+ goto exception;
+ }
+ }
+ if (n == 0) {
+ JS_FreeValue(ctx, A);
+ A = JS_NULL;
+ }
+ }
+ JS_FreeValue(ctx, result);
+ JS_FreeValue(ctx, S);
+ return A;
+
+exception:
+ JS_FreeValue(ctx, A);
+ JS_FreeValue(ctx, result);
+ JS_FreeValue(ctx, S);
+ return JS_EXCEPTION;
+}
+
+typedef struct JSRegExpStringIteratorData {
+ JSValue iterating_regexp;
+ JSValue iterated_string;
+ BOOL global;
+ BOOL unicode;
+ BOOL done;
+} JSRegExpStringIteratorData;
+
+static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
+ if (it) {
+ JS_FreeValueRT(rt, it->iterating_regexp);
+ JS_FreeValueRT(rt, it->iterated_string);
+ js_free_rt(rt, it);
+ }
+}
+
+static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
+ if (it) {
+ JS_MarkValue(rt, it->iterating_regexp, mark_func);
+ JS_MarkValue(rt, it->iterated_string, mark_func);
+ }
+}
+
+static JSValue js_regexp_string_iterator_next(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSRegExpStringIteratorData *it;
+ JSValueConst R, S;
+ JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;
+ JSString *sp;
+
+ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);
+ if (!it)
+ goto exception;
+ if (it->done) {
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ }
+ R = it->iterating_regexp;
+ S = it->iterated_string;
+ match = JS_RegExpExec(ctx, R, S);
+ if (JS_IsException(match))
+ goto exception;
+ if (JS_IsNull(match)) {
+ it->done = TRUE;
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ } else if (it->global) {
+ matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));
+ if (JS_IsException(matchStr))
+ goto exception;
+ if (JS_IsEmptyString(matchStr)) {
+ int64_t thisIndex, nextIndex;
+ if (JS_ToLengthFree(ctx, &thisIndex,
+ JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)
+ goto exception;
+ sp = JS_VALUE_GET_STRING(S);
+ nextIndex = string_advance_index(sp, thisIndex, it->unicode);
+ if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,
+ JS_NewInt64(ctx, nextIndex)) < 0)
+ goto exception;
+ }
+ JS_FreeValue(ctx, matchStr);
+ } else {
+ it->done = TRUE;
+ }
+ *pdone = FALSE;
+ return match;
+ exception:
+ JS_FreeValue(ctx, match);
+ JS_FreeValue(ctx, matchStr);
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+}
+
+static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // [Symbol.matchAll](str)
+ JSValueConst R = this_val;
+ JSValue S, C, flags, matcher, iter;
+ JSValueConst args[2];
+ JSString *strp;
+ int64_t lastIndex;
+ JSRegExpStringIteratorData *it;
+
+ if (!JS_IsObject(R))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ C = JS_UNDEFINED;
+ flags = JS_UNDEFINED;
+ matcher = JS_UNDEFINED;
+ iter = JS_UNDEFINED;
+
+ S = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(S))
+ goto exception;
+ C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);
+ if (JS_IsException(C))
+ goto exception;
+ flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));
+ if (JS_IsException(flags))
+ goto exception;
+ args[0] = R;
+ args[1] = flags;
+ matcher = JS_CallConstructor(ctx, C, 2, args);
+ if (JS_IsException(matcher))
+ goto exception;
+ if (JS_ToLengthFree(ctx, &lastIndex,
+ JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))
+ goto exception;
+ if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,
+ JS_NewInt64(ctx, lastIndex)) < 0)
+ goto exception;
+
+ iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);
+ if (JS_IsException(iter))
+ goto exception;
+ it = js_malloc(ctx, sizeof(*it));
+ if (!it)
+ goto exception;
+ it->iterating_regexp = matcher;
+ it->iterated_string = S;
+ strp = JS_VALUE_GET_STRING(flags);
+ it->global = string_indexof_char(strp, 'g', 0) >= 0;
+ it->unicode = string_indexof_char(strp, 'u', 0) >= 0;
+ it->done = FALSE;
+ JS_SetOpaque(iter, it);
+
+ JS_FreeValue(ctx, C);
+ JS_FreeValue(ctx, flags);
+ return iter;
+ exception:
+ JS_FreeValue(ctx, S);
+ JS_FreeValue(ctx, C);
+ JS_FreeValue(ctx, flags);
+ JS_FreeValue(ctx, matcher);
+ JS_FreeValue(ctx, iter);
+ return JS_EXCEPTION;
+}
+
+typedef struct ValueBuffer {
+ JSContext *ctx;
+ JSValue *arr;
+ JSValue def[4];
+ int len;
+ int size;
+ int error_status;
+} ValueBuffer;
+
+static int value_buffer_init(JSContext *ctx, ValueBuffer *b)
+{
+ b->ctx = ctx;
+ b->len = 0;
+ b->size = 4;
+ b->error_status = 0;
+ b->arr = b->def;
+ return 0;
+}
+
+static void value_buffer_free(ValueBuffer *b)
+{
+ while (b->len > 0)
+ JS_FreeValue(b->ctx, b->arr[--b->len]);
+ if (b->arr != b->def)
+ js_free(b->ctx, b->arr);
+ b->arr = b->def;
+ b->size = 4;
+}
+
+static int value_buffer_append(ValueBuffer *b, JSValue val)
+{
+ if (b->error_status)
+ return -1;
+
+ if (b->len >= b->size) {
+ int new_size = (b->len + (b->len >> 1) + 31) & ~16;
+ size_t slack;
+ JSValue *new_arr;
+
+ if (b->arr == b->def) {
+ new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);
+ if (new_arr)
+ memcpy(new_arr, b->def, sizeof b->def);
+ } else {
+ new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);
+ }
+ if (!new_arr) {
+ value_buffer_free(b);
+ JS_FreeValue(b->ctx, val);
+ b->error_status = -1;
+ return -1;
+ }
+ new_size += slack / sizeof(*new_arr);
+ b->arr = new_arr;
+ b->size = new_size;
+ }
+ b->arr[b->len++] = val;
+ return 0;
+}
+
+static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)
+{
+ JSValue val;
+ int res;
+
+ val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);
+ if (JS_IsException(val))
+ return -1;
+ // rx.constructor === RegExp
+ res = js_same_value(ctx, val, ctx->regexp_ctor);
+ JS_FreeValue(ctx, val);
+ if (res) {
+ val = JS_GetProperty(ctx, rx, JS_ATOM_exec);
+ if (JS_IsException(val))
+ return -1;
+ // rx.exec === RE_exec
+ res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);
+ JS_FreeValue(ctx, val);
+ }
+ return res;
+}
+
+static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // [Symbol.replace](str, rep)
+ JSValueConst rx = this_val, rep = argv[1];
+ JSValueConst args[6];
+ JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res;
+ JSString *sp, *rp;
+ StringBuffer b_s, *b = &b_s;
+ ValueBuffer v_b, *results = &v_b;
+ int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;
+ uint32_t nCaptures;
+ int64_t position;
+
+ if (!JS_IsObject(rx))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ string_buffer_init(ctx, b, 0);
+ value_buffer_init(ctx, results);
+
+ rep_val = JS_UNDEFINED;
+ matched = JS_UNDEFINED;
+ tab = JS_UNDEFINED;
+ rep_str = JS_UNDEFINED;
+ namedCaptures = JS_UNDEFINED;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ goto exception;
+
+ sp = JS_VALUE_GET_STRING(str);
+ rp = NULL;
+ functionalReplace = JS_IsFunction(ctx, rep);
+ if (!functionalReplace) {
+ rep_val = JS_ToString(ctx, rep);
+ if (JS_IsException(rep_val))
+ goto exception;
+ rp = JS_VALUE_GET_STRING(rep_val);
+ }
+ fullUnicode = 0;
+ is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
+ if (is_global < 0)
+ goto exception;
+ if (is_global) {
+ fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
+ if (fullUnicode < 0)
+ goto exception;
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
+ goto exception;
+ }
+
+ if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {
+ /* use faster version for simple cases */
+ res = JS_RegExpDelete(ctx, rx, str);
+ goto done;
+ }
+ for(;;) {
+ JSValue result;
+ result = JS_RegExpExec(ctx, rx, str);
+ if (JS_IsException(result))
+ goto exception;
+ if (JS_IsNull(result))
+ break;
+ if (value_buffer_append(results, result) < 0)
+ goto exception;
+ if (!is_global)
+ break;
+ JS_FreeValue(ctx, matched);
+ matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
+ if (JS_IsException(matched))
+ goto exception;
+ if (JS_IsEmptyString(matched)) {
+ /* always advance of at least one char */
+ int64_t thisIndex, nextIndex;
+ if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
+ goto exception;
+ nextIndex = string_advance_index(sp, thisIndex, fullUnicode);
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
+ goto exception;
+ }
+ }
+ nextSourcePosition = 0;
+ for(j = 0; j < results->len; j++) {
+ JSValueConst result;
+ result = results->arr[j];
+ if (js_get_length32(ctx, &nCaptures, result) < 0)
+ goto exception;
+ JS_FreeValue(ctx, matched);
+ matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
+ if (JS_IsException(matched))
+ goto exception;
+ if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))
+ goto exception;
+ if (position > sp->len)
+ position = sp->len;
+ else if (position < 0)
+ position = 0;
+ /* ignore substition if going backward (can happen
+ with custom regexp object) */
+ JS_FreeValue(ctx, tab);
+ tab = JS_NewArray(ctx);
+ if (JS_IsException(tab))
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ for(n = 1; n < nCaptures; n++) {
+ JSValue capN;
+ capN = JS_GetPropertyInt64(ctx, result, n);
+ if (JS_IsException(capN))
+ goto exception;
+ if (!JS_IsUndefined(capN)) {
+ capN = JS_ToStringFree(ctx, capN);
+ if (JS_IsException(capN))
+ goto exception;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ JS_FreeValue(ctx, namedCaptures);
+ namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);
+ if (JS_IsException(namedCaptures))
+ goto exception;
+ if (functionalReplace) {
+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ if (!JS_IsUndefined(namedCaptures)) {
+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ args[0] = JS_UNDEFINED;
+ args[1] = tab;
+ JS_FreeValue(ctx, rep_str);
+ rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));
+ } else {
+ JSValue namedCaptures1;
+ if (!JS_IsUndefined(namedCaptures)) {
+ namedCaptures1 = JS_ToObject(ctx, namedCaptures);
+ if (JS_IsException(namedCaptures1))
+ goto exception;
+ } else {
+ namedCaptures1 = JS_UNDEFINED;
+ }
+ args[0] = matched;
+ args[1] = str;
+ args[2] = JS_NewInt32(ctx, position);
+ args[3] = tab;
+ args[4] = namedCaptures1;
+ args[5] = rep_val;
+ JS_FreeValue(ctx, rep_str);
+ rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
+ JS_FreeValue(ctx, namedCaptures1);
+ }
+ if (JS_IsException(rep_str))
+ goto exception;
+ if (position >= nextSourcePosition) {
+ string_buffer_concat(b, sp, nextSourcePosition, position);
+ string_buffer_concat_value(b, rep_str);
+ nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;
+ }
+ }
+ string_buffer_concat(b, sp, nextSourcePosition, sp->len);
+ res = string_buffer_end(b);
+ goto done1;
+
+exception:
+ res = JS_EXCEPTION;
+done:
+ string_buffer_free(b);
+done1:
+ value_buffer_free(results);
+ JS_FreeValue(ctx, rep_val);
+ JS_FreeValue(ctx, matched);
+ JS_FreeValue(ctx, tab);
+ JS_FreeValue(ctx, rep_str);
+ JS_FreeValue(ctx, namedCaptures);
+ JS_FreeValue(ctx, str);
+ return res;
+}
+
+static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst rx = this_val;
+ JSValue str, previousLastIndex, currentLastIndex, result, index;
+
+ if (!JS_IsObject(rx))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ result = JS_UNDEFINED;
+ currentLastIndex = JS_UNDEFINED;
+ previousLastIndex = JS_UNDEFINED;
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ goto exception;
+
+ previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
+ if (JS_IsException(previousLastIndex))
+ goto exception;
+
+ if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {
+ goto exception;
+ }
+ }
+ result = JS_RegExpExec(ctx, rx, str);
+ if (JS_IsException(result))
+ goto exception;
+ currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
+ if (JS_IsException(currentLastIndex))
+ goto exception;
+ if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {
+ JS_FreeValue(ctx, previousLastIndex);
+ } else {
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {
+ previousLastIndex = JS_UNDEFINED;
+ goto exception;
+ }
+ }
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, currentLastIndex);
+
+ if (JS_IsNull(result)) {
+ return JS_NewInt32(ctx, -1);
+ } else {
+ index = JS_GetProperty(ctx, result, JS_ATOM_index);
+ JS_FreeValue(ctx, result);
+ return index;
+ }
+
+exception:
+ JS_FreeValue(ctx, result);
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, currentLastIndex);
+ JS_FreeValue(ctx, previousLastIndex);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // [Symbol.split](str, limit)
+ JSValueConst rx = this_val;
+ JSValueConst args[2];
+ JSValue str, ctor, splitter, A, flags, z, sub;
+ JSString *strp;
+ uint32_t lim, size, p, q;
+ int unicodeMatching;
+ int64_t lengthA, e, numberOfCaptures, i;
+
+ if (!JS_IsObject(rx))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ ctor = JS_UNDEFINED;
+ splitter = JS_UNDEFINED;
+ A = JS_UNDEFINED;
+ flags = JS_UNDEFINED;
+ z = JS_UNDEFINED;
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ goto exception;
+ ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);
+ if (JS_IsException(ctor))
+ goto exception;
+ flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));
+ if (JS_IsException(flags))
+ goto exception;
+ strp = JS_VALUE_GET_STRING(flags);
+ unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;
+ if (string_indexof_char(strp, 'y', 0) < 0) {
+ flags = JS_ConcatString3(ctx, "", flags, "y");
+ if (JS_IsException(flags))
+ goto exception;
+ }
+ args[0] = rx;
+ args[1] = flags;
+ splitter = JS_CallConstructor(ctx, ctor, 2, args);
+ if (JS_IsException(splitter))
+ goto exception;
+ A = JS_NewArray(ctx);
+ if (JS_IsException(A))
+ goto exception;
+ lengthA = 0;
+ if (JS_IsUndefined(argv[1])) {
+ lim = 0xffffffff;
+ } else {
+ if (JS_ToUint32(ctx, &lim, argv[1]) < 0)
+ goto exception;
+ if (lim == 0)
+ goto done;
+ }
+ strp = JS_VALUE_GET_STRING(str);
+ p = q = 0;
+ size = strp->len;
+ if (size == 0) {
+ z = JS_RegExpExec(ctx, splitter, str);
+ if (JS_IsException(z))
+ goto exception;
+ if (JS_IsNull(z))
+ goto add_tail;
+ goto done;
+ }
+ while (q < size) {
+ if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)
+ goto exception;
+ JS_FreeValue(ctx, z);
+ z = JS_RegExpExec(ctx, splitter, str);
+ if (JS_IsException(z))
+ goto exception;
+ if (JS_IsNull(z)) {
+ q = string_advance_index(strp, q, unicodeMatching);
+ } else {
+ if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))
+ goto exception;
+ if (e > size)
+ e = size;
+ if (e == p) {
+ q = string_advance_index(strp, q, unicodeMatching);
+ } else {
+ sub = js_sub_string(ctx, strp, p, q);
+ if (JS_IsException(sub))
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ if (lengthA == lim)
+ goto done;
+ p = e;
+ if (js_get_length64(ctx, &numberOfCaptures, z))
+ goto exception;
+ for(i = 1; i < numberOfCaptures; i++) {
+ sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));
+ if (JS_IsException(sub))
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ if (lengthA == lim)
+ goto done;
+ }
+ q = p;
+ }
+ }
+ }
+add_tail:
+ if (p > size)
+ p = size;
+ sub = js_sub_string(ctx, strp, p, size);
+ if (JS_IsException(sub))
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ goto done;
+exception:
+ JS_FreeValue(ctx, A);
+ A = JS_EXCEPTION;
+done:
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, ctor);
+ JS_FreeValue(ctx, splitter);
+ JS_FreeValue(ctx, flags);
+ JS_FreeValue(ctx, z);
+ return A;
+}
+
+static const JSCFunctionListEntry js_regexp_funcs[] = {
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+ //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),
+ //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),
+};
+
+static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
+ JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
+ JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
+ JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),
+ JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),
+ JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),
+ JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),
+ JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),
+ JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),
+ JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
+ JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
+ JS_CFUNC_DEF("test", 1, js_regexp_test ),
+ JS_CFUNC_DEF("toString", 0, js_regexp_toString ),
+ JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),
+ JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),
+ JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),
+ JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),
+ JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),
+ //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),
+ //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),
+};
+
+static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)
+{
+ ctx->compile_regexp = js_compile_regexp;
+}
+
+void JS_AddIntrinsicRegExp(JSContext *ctx)
+{
+ JSValueConst obj;
+
+ JS_AddIntrinsicRegExpCompiler(ctx);
+
+ ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,
+ countof(js_regexp_proto_funcs));
+ obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,
+ ctx->class_proto[JS_CLASS_REGEXP]);
+ ctx->regexp_ctor = JS_DupValue(ctx, obj);
+ JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));
+
+ ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =
+ JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],
+ js_regexp_string_iterator_proto_funcs,
+ countof(js_regexp_string_iterator_proto_funcs));
+}
+
+/* JSON */
+
+static int json_parse_expect(JSParseState *s, int tok)
+{
+ if (s->token.val != tok) {
+ /* XXX: dump token correctly in all cases */
+ return js_parse_error(s, "expecting '%c'", tok);
+ }
+ return json_next_token(s);
+}
+
+static JSValue json_parse_value(JSParseState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue val = JS_NULL;
+ int ret;
+
+ switch(s->token.val) {
+ case '{':
+ {
+ JSValue prop_val;
+ JSAtom prop_name;
+
+ if (json_next_token(s))
+ goto fail;
+ val = JS_NewObject(ctx);
+ if (JS_IsException(val))
+ goto fail;
+ if (s->token.val != '}') {
+ for(;;) {
+ if (s->token.val == TOK_STRING) {
+ prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
+ if (prop_name == JS_ATOM_NULL)
+ goto fail;
+ } else if (s->ext_json && s->token.val == TOK_IDENT) {
+ prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ } else {
+ js_parse_error(s, "expecting property name");
+ goto fail;
+ }
+ if (json_next_token(s))
+ goto fail1;
+ if (json_parse_expect(s, ':'))
+ goto fail1;
+ prop_val = json_parse_value(s);
+ if (JS_IsException(prop_val)) {
+ fail1:
+ JS_FreeAtom(ctx, prop_name);
+ goto fail;
+ }
+ ret = JS_DefinePropertyValue(ctx, val, prop_name,
+ prop_val, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, prop_name);
+ if (ret < 0)
+ goto fail;
+
+ if (s->token.val != ',')
+ break;
+ if (json_next_token(s))
+ goto fail;
+ if (s->ext_json && s->token.val == '}')
+ break;
+ }
+ }
+ if (json_parse_expect(s, '}'))
+ goto fail;
+ }
+ break;
+ case '[':
+ {
+ JSValue el;
+ uint32_t idx;
+
+ if (json_next_token(s))
+ goto fail;
+ val = JS_NewArray(ctx);
+ if (JS_IsException(val))
+ goto fail;
+ if (s->token.val != ']') {
+ idx = 0;
+ for(;;) {
+ el = json_parse_value(s);
+ if (JS_IsException(el))
+ goto fail;
+ ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
+ if (ret < 0)
+ goto fail;
+ if (s->token.val != ',')
+ break;
+ if (json_next_token(s))
+ goto fail;
+ idx++;
+ if (s->ext_json && s->token.val == ']')
+ break;
+ }
+ }
+ if (json_parse_expect(s, ']'))
+ goto fail;
+ }
+ break;
+ case TOK_STRING:
+ val = JS_DupValue(ctx, s->token.u.str.str);
+ if (json_next_token(s))
+ goto fail;
+ break;
+ case TOK_NUMBER:
+ val = s->token.u.num.val;
+ if (json_next_token(s))
+ goto fail;
+ break;
+ case TOK_IDENT:
+ if (s->token.u.ident.atom == JS_ATOM_false ||
+ s->token.u.ident.atom == JS_ATOM_true) {
+ val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));
+ } else if (s->token.u.ident.atom == JS_ATOM_null) {
+ val = JS_NULL;
+ } else {
+ goto def_token;
+ }
+ if (json_next_token(s))
+ goto fail;
+ break;
+ default:
+ def_token:
+ if (s->token.val == TOK_EOF) {
+ js_parse_error(s, "unexpected end of input");
+ } else {
+ js_parse_error(s, "unexpected token: '%.*s'",
+ (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
+ }
+ goto fail;
+ }
+ return val;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename, int flags)
+{
+ JSParseState s1, *s = &s1;
+ JSValue val = JS_UNDEFINED;
+
+ js_parse_init(ctx, s, buf, buf_len, filename, 1);
+ s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
+ if (json_next_token(s))
+ goto fail;
+ val = json_parse_value(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (s->token.val != TOK_EOF) {
+ if (js_parse_error(s, "unexpected data at the end"))
+ goto fail;
+ }
+ return val;
+ fail:
+ JS_FreeValue(ctx, val);
+ free_token(s, &s->token);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename)
+{
+ return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
+}
+
+static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
+ JSAtom name, JSValueConst reviver)
+{
+ JSValue val, new_el, name_val, res;
+ JSValueConst args[2];
+ int ret, is_array;
+ uint32_t i, len = 0;
+ JSAtom prop;
+ JSPropertyEnum *atoms = NULL;
+
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ return JS_ThrowStackOverflow(ctx);
+ }
+
+ val = JS_GetProperty(ctx, holder, name);
+ if (JS_IsException(val))
+ return val;
+ if (JS_IsObject(val)) {
+ is_array = JS_IsArray(ctx, val);
+ if (is_array < 0)
+ goto fail;
+ if (is_array) {
+ if (js_get_length32(ctx, &len, val))
+ goto fail;
+ } else {
+ ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
+ if (ret < 0)
+ goto fail;
+ }
+ for(i = 0; i < len; i++) {
+ if (is_array) {
+ prop = JS_NewAtomUInt32(ctx, i);
+ if (prop == JS_ATOM_NULL)
+ goto fail;
+ } else {
+ prop = JS_DupAtom(ctx, atoms[i].atom);
+ }
+ new_el = internalize_json_property(ctx, val, prop, reviver);
+ if (JS_IsException(new_el)) {
+ JS_FreeAtom(ctx, prop);
+ goto fail;
+ }
+ if (JS_IsUndefined(new_el)) {
+ ret = JS_DeleteProperty(ctx, val, prop, 0);
+ } else {
+ ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);
+ }
+ JS_FreeAtom(ctx, prop);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+ js_free_prop_enum(ctx, atoms, len);
+ atoms = NULL;
+ name_val = JS_AtomToValue(ctx, name);
+ if (JS_IsException(name_val))
+ goto fail;
+ args[0] = name_val;
+ args[1] = val;
+ res = JS_Call(ctx, reviver, holder, 2, args);
+ JS_FreeValue(ctx, name_val);
+ JS_FreeValue(ctx, val);
+ return res;
+ fail:
+ js_free_prop_enum(ctx, atoms, len);
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, root;
+ JSValueConst reviver;
+ const char *str;
+ size_t len;
+
+ str = JS_ToCStringLen(ctx, &len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ obj = JS_ParseJSON(ctx, str, len, "<input>");
+ JS_FreeCString(ctx, str);
+ if (JS_IsException(obj))
+ return obj;
+ if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
+ reviver = argv[1];
+ root = JS_NewObject(ctx);
+ if (JS_IsException(root)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
+ JS_PROP_C_W_E) < 0) {
+ JS_FreeValue(ctx, root);
+ return JS_EXCEPTION;
+ }
+ obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
+ reviver);
+ JS_FreeValue(ctx, root);
+ }
+ return obj;
+}
+
+typedef struct JSONStringifyContext {
+ JSValueConst replacer_func;
+ JSValue stack;
+ JSValue property_list;
+ JSValue gap;
+ JSValue empty;
+ StringBuffer *b;
+} JSONStringifyContext;
+
+static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {
+ JSValue r = JS_ToQuotedString(ctx, val);
+ JS_FreeValue(ctx, val);
+ return r;
+}
+
+static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
+ JSValueConst holder, JSValue val, JSValueConst key)
+{
+ JSValue v;
+ JSValueConst args[2];
+
+ if (JS_IsObject(val)
+#ifdef CONFIG_BIGNUM
+ || JS_IsBigInt(ctx, val) /* XXX: probably useless */
+#endif
+ ) {
+ JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
+ if (JS_IsException(f))
+ goto exception;
+ if (JS_IsFunction(ctx, f)) {
+ v = JS_CallFree(ctx, f, val, 1, &key);
+ JS_FreeValue(ctx, val);
+ val = v;
+ if (JS_IsException(val))
+ goto exception;
+ } else {
+ JS_FreeValue(ctx, f);
+ }
+ }
+
+ if (!JS_IsUndefined(jsc->replacer_func)) {
+ args[0] = key;
+ args[1] = val;
+ v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);
+ JS_FreeValue(ctx, val);
+ val = v;
+ if (JS_IsException(val))
+ goto exception;
+ }
+
+ switch (JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_OBJECT:
+ if (JS_IsFunction(ctx, val))
+ break;
+ case JS_TAG_STRING:
+ case JS_TAG_INT:
+ case JS_TAG_FLOAT64:
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+#endif
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+#endif
+ case JS_TAG_EXCEPTION:
+ return val;
+ default:
+ break;
+ }
+ JS_FreeValue(ctx, val);
+ return JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
+ JSValueConst holder, JSValue val,
+ JSValueConst indent)
+{
+ JSValue indent1, sep, sep1, tab, v, prop;
+ JSObject *p;
+ int64_t i, len;
+ int cl, ret;
+ BOOL has_content;
+
+ indent1 = JS_UNDEFINED;
+ sep = JS_UNDEFINED;
+ sep1 = JS_UNDEFINED;
+ tab = JS_UNDEFINED;
+ prop = JS_UNDEFINED;
+
+ switch (JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_OBJECT:
+ p = JS_VALUE_GET_OBJ(val);
+ cl = p->class_id;
+ if (cl == JS_CLASS_STRING) {
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ goto exception;
+ val = JS_ToQuotedStringFree(ctx, val);
+ if (JS_IsException(val))
+ goto exception;
+ return string_buffer_concat_value_free(jsc->b, val);
+ } else if (cl == JS_CLASS_NUMBER) {
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ goto exception;
+ return string_buffer_concat_value_free(jsc->b, val);
+ } else if (cl == JS_CLASS_BOOLEAN) {
+ ret = string_buffer_concat_value(jsc->b, p->u.object_data);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+#ifdef CONFIG_BIGNUM
+ else if (cl == JS_CLASS_BIG_FLOAT) {
+ return string_buffer_concat_value_free(jsc->b, val);
+ } else if (cl == JS_CLASS_BIG_INT) {
+ JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
+ goto exception;
+ }
+#endif
+ v = js_array_includes(ctx, jsc->stack, 1, &val);
+ if (JS_IsException(v))
+ goto exception;
+ if (JS_ToBoolFree(ctx, v)) {
+ JS_ThrowTypeError(ctx, "circular reference");
+ goto exception;
+ }
+ indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));
+ if (JS_IsException(indent1))
+ goto exception;
+ if (!JS_IsEmptyString(jsc->gap)) {
+ sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");
+ if (JS_IsException(sep))
+ goto exception;
+ sep1 = JS_NewString(ctx, " ");
+ if (JS_IsException(sep1))
+ goto exception;
+ } else {
+ sep = JS_DupValue(ctx, jsc->empty);
+ sep1 = JS_DupValue(ctx, jsc->empty);
+ }
+ v = js_array_push(ctx, jsc->stack, 1, &val, 0);
+ if (check_exception_free(ctx, v))
+ goto exception;
+ ret = JS_IsArray(ctx, val);
+ if (ret < 0)
+ goto exception;
+ if (ret) {
+ if (js_get_length64(ctx, &len, val))
+ goto exception;
+ string_buffer_putc8(jsc->b, '[');
+ for(i = 0; i < len; i++) {
+ if (i > 0)
+ string_buffer_putc8(jsc->b, ',');
+ string_buffer_concat_value(jsc->b, sep);
+ v = JS_GetPropertyInt64(ctx, val, i);
+ if (JS_IsException(v))
+ goto exception;
+ /* XXX: could do this string conversion only when needed */
+ prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));
+ if (JS_IsException(prop))
+ goto exception;
+ v = js_json_check(ctx, jsc, val, v, prop);
+ JS_FreeValue(ctx, prop);
+ prop = JS_UNDEFINED;
+ if (JS_IsException(v))
+ goto exception;
+ if (JS_IsUndefined(v))
+ v = JS_NULL;
+ if (js_json_to_str(ctx, jsc, val, v, indent1))
+ goto exception;
+ }
+ if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
+ string_buffer_putc8(jsc->b, '\n');
+ string_buffer_concat_value(jsc->b, indent);
+ }
+ string_buffer_putc8(jsc->b, ']');
+ } else {
+ if (!JS_IsUndefined(jsc->property_list))
+ tab = JS_DupValue(ctx, jsc->property_list);
+ else
+ tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY);
+ if (JS_IsException(tab))
+ goto exception;
+ if (js_get_length64(ctx, &len, tab))
+ goto exception;
+ string_buffer_putc8(jsc->b, '{');
+ has_content = FALSE;
+ for(i = 0; i < len; i++) {
+ JS_FreeValue(ctx, prop);
+ prop = JS_GetPropertyInt64(ctx, tab, i);
+ if (JS_IsException(prop))
+ goto exception;
+ v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));
+ if (JS_IsException(v))
+ goto exception;
+ v = js_json_check(ctx, jsc, val, v, prop);
+ if (JS_IsException(v))
+ goto exception;
+ if (!JS_IsUndefined(v)) {
+ if (has_content)
+ string_buffer_putc8(jsc->b, ',');
+ prop = JS_ToQuotedStringFree(ctx, prop);
+ if (JS_IsException(prop)) {
+ JS_FreeValue(ctx, v);
+ goto exception;
+ }
+ string_buffer_concat_value(jsc->b, sep);
+ string_buffer_concat_value(jsc->b, prop);
+ string_buffer_putc8(jsc->b, ':');
+ string_buffer_concat_value(jsc->b, sep1);
+ if (js_json_to_str(ctx, jsc, val, v, indent1))
+ goto exception;
+ has_content = TRUE;
+ }
+ }
+ if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {
+ string_buffer_putc8(jsc->b, '\n');
+ string_buffer_concat_value(jsc->b, indent);
+ }
+ string_buffer_putc8(jsc->b, '}');
+ }
+ if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
+ goto exception;
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, tab);
+ JS_FreeValue(ctx, sep);
+ JS_FreeValue(ctx, sep1);
+ JS_FreeValue(ctx, indent1);
+ JS_FreeValue(ctx, prop);
+ return 0;
+ case JS_TAG_STRING:
+ val = JS_ToQuotedStringFree(ctx, val);
+ if (JS_IsException(val))
+ goto exception;
+ goto concat_value;
+ case JS_TAG_FLOAT64:
+ if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
+ val = JS_NULL;
+ }
+ goto concat_value;
+ case JS_TAG_INT:
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+#endif
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ concat_value:
+ return string_buffer_concat_value_free(jsc->b, val);
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
+ goto exception;
+#endif
+ default:
+ JS_FreeValue(ctx, val);
+ return 0;
+ }
+
+exception:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, tab);
+ JS_FreeValue(ctx, sep);
+ JS_FreeValue(ctx, sep1);
+ JS_FreeValue(ctx, indent1);
+ JS_FreeValue(ctx, prop);
+ return -1;
+}
+
+JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
+ JSValueConst replacer, JSValueConst space0)
+{
+ StringBuffer b_s;
+ JSONStringifyContext jsc_s, *jsc = &jsc_s;
+ JSValue val, v, space, ret, wrapper;
+ int res;
+ int64_t i, j, n;
+
+ jsc->replacer_func = JS_UNDEFINED;
+ jsc->stack = JS_UNDEFINED;
+ jsc->property_list = JS_UNDEFINED;
+ jsc->gap = JS_UNDEFINED;
+ jsc->b = &b_s;
+ jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ ret = JS_UNDEFINED;
+ wrapper = JS_UNDEFINED;
+
+ string_buffer_init(ctx, jsc->b, 0);
+ jsc->stack = JS_NewArray(ctx);
+ if (JS_IsException(jsc->stack))
+ goto exception;
+ if (JS_IsFunction(ctx, replacer)) {
+ jsc->replacer_func = replacer;
+ } else {
+ res = JS_IsArray(ctx, replacer);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ /* XXX: enumeration is not fully correct */
+ jsc->property_list = JS_NewArray(ctx);
+ if (JS_IsException(jsc->property_list))
+ goto exception;
+ if (js_get_length64(ctx, &n, replacer))
+ goto exception;
+ for (i = j = 0; i < n; i++) {
+ JSValue present;
+ v = JS_GetPropertyInt64(ctx, replacer, i);
+ if (JS_IsException(v))
+ goto exception;
+ if (JS_IsObject(v)) {
+ JSObject *p = JS_VALUE_GET_OBJ(v);
+ if (p->class_id == JS_CLASS_STRING ||
+ p->class_id == JS_CLASS_NUMBER) {
+ v = JS_ToStringFree(ctx, v);
+ if (JS_IsException(v))
+ goto exception;
+ } else {
+ JS_FreeValue(ctx, v);
+ continue;
+ }
+ } else if (JS_IsNumber(v)) {
+ v = JS_ToStringFree(ctx, v);
+ if (JS_IsException(v))
+ goto exception;
+ } else if (!JS_IsString(v)) {
+ JS_FreeValue(ctx, v);
+ continue;
+ }
+ present = js_array_includes(ctx, jsc->property_list,
+ 1, &v);
+ if (JS_IsException(present)) {
+ JS_FreeValue(ctx, v);
+ goto exception;
+ }
+ if (!JS_ToBoolFree(ctx, present)) {
+ JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);
+ } else {
+ JS_FreeValue(ctx, v);
+ }
+ }
+ }
+ }
+ space = JS_DupValue(ctx, space0);
+ if (JS_IsObject(space)) {
+ JSObject *p = JS_VALUE_GET_OBJ(space);
+ if (p->class_id == JS_CLASS_NUMBER) {
+ space = JS_ToNumberFree(ctx, space);
+ } else if (p->class_id == JS_CLASS_STRING) {
+ space = JS_ToStringFree(ctx, space);
+ }
+ if (JS_IsException(space)) {
+ JS_FreeValue(ctx, space);
+ goto exception;
+ }
+ }
+ if (JS_IsNumber(space)) {
+ int n;
+ if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))
+ goto exception;
+ jsc->gap = JS_NewStringLen(ctx, " ", n);
+ } else if (JS_IsString(space)) {
+ JSString *p = JS_VALUE_GET_STRING(space);
+ jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));
+ } else {
+ jsc->gap = JS_DupValue(ctx, jsc->empty);
+ }
+ JS_FreeValue(ctx, space);
+ if (JS_IsException(jsc->gap))
+ goto exception;
+ wrapper = JS_NewObject(ctx);
+ if (JS_IsException(wrapper))
+ goto exception;
+ if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,
+ JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)
+ goto exception;
+ val = JS_DupValue(ctx, obj);
+
+ val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);
+ if (JS_IsException(val))
+ goto exception;
+ if (JS_IsUndefined(val)) {
+ ret = JS_UNDEFINED;
+ goto done1;
+ }
+ if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))
+ goto exception;
+
+ ret = string_buffer_end(jsc->b);
+ goto done;
+
+exception:
+ ret = JS_EXCEPTION;
+done1:
+ string_buffer_free(jsc->b);
+done:
+ JS_FreeValue(ctx, wrapper);
+ JS_FreeValue(ctx, jsc->empty);
+ JS_FreeValue(ctx, jsc->gap);
+ JS_FreeValue(ctx, jsc->property_list);
+ JS_FreeValue(ctx, jsc->stack);
+ return ret;
+}
+
+static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // stringify(val, replacer, space)
+ return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
+}
+
+static const JSCFunctionListEntry js_json_funcs[] = {
+ JS_CFUNC_DEF("parse", 2, js_json_parse ),
+ JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_json_obj[] = {
+ JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+void JS_AddIntrinsicJSON(JSContext *ctx)
+{
+ /* add JSON as autoinit object */
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));
+}
+
+/* Reflect */
+
+static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);
+}
+
+static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst func, array_arg, new_target;
+ JSValue *tab, ret;
+ uint32_t len;
+
+ func = argv[0];
+ array_arg = argv[1];
+ if (argc > 2) {
+ new_target = argv[2];
+ if (!JS_IsConstructor(ctx, new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ } else {
+ new_target = func;
+ }
+ tab = build_arg_list(ctx, &len, array_arg);
+ if (!tab)
+ return JS_EXCEPTION;
+ ret = JS_CallConstructor2(ctx, func, new_target, len, tab);
+ free_arg_list(ctx, tab, len);
+ return ret;
+}
+
+static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj;
+ JSAtom atom;
+ int ret;
+
+ obj = argv[0];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ atom = JS_ValueToAtom(ctx, argv[1]);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_DeleteProperty(ctx, obj, atom, 0);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj, prop, receiver;
+ JSAtom atom;
+ JSValue ret;
+
+ obj = argv[0];
+ prop = argv[1];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ if (argc > 2)
+ receiver = argv[2];
+ else
+ receiver = obj;
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj, prop;
+ JSAtom atom;
+ int ret;
+
+ obj = argv[0];
+ prop = argv[1];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_HasProperty(ctx, obj, atom);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj, prop, val, receiver;
+ int ret;
+ JSAtom atom;
+
+ obj = argv[0];
+ prop = argv[1];
+ val = argv[2];
+ if (argc > 3)
+ receiver = argv[3];
+ else
+ receiver = obj;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_SetPropertyGeneric(ctx, obj, atom,
+ JS_DupValue(ctx, val), receiver, 0);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ return JS_GetOwnPropertyNames2(ctx, argv[0],
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,
+ JS_ITERATOR_KIND_KEY);
+}
+
+static const JSCFunctionListEntry js_reflect_funcs[] = {
+ JS_CFUNC_DEF("apply", 3, js_reflect_apply ),
+ JS_CFUNC_DEF("construct", 2, js_reflect_construct ),
+ JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),
+ JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),
+ JS_CFUNC_DEF("get", 2, js_reflect_get ),
+ JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),
+ JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),
+ JS_CFUNC_DEF("has", 2, js_reflect_has ),
+ JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),
+ JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),
+ JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
+ JS_CFUNC_DEF("set", 3, js_reflect_set ),
+ JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_reflect_obj[] = {
+ JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+/* Proxy */
+
+static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
+ if (s) {
+ JS_FreeValueRT(rt, s->target);
+ JS_FreeValueRT(rt, s->handler);
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
+ if (s) {
+ JS_MarkValue(rt, s->target, mark_func);
+ JS_MarkValue(rt, s->handler, mark_func);
+ }
+}
+
+static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "revoked proxy");
+}
+
+static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
+ JSValueConst obj, JSAtom name)
+{
+ JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
+ JSValue method;
+
+ /* safer to test recursion in all proxy methods */
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ JS_ThrowStackOverflow(ctx);
+ return NULL;
+ }
+
+ /* 's' should never be NULL */
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ return NULL;
+ }
+ method = JS_GetProperty(ctx, s->handler, name);
+ if (JS_IsException(method))
+ return NULL;
+ if (JS_IsNull(method))
+ method = JS_UNDEFINED;
+ *pmethod = method;
+ return s;
+}
+
+static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
+{
+ JSProxyData *s;
+ JSValue method, ret, proto1;
+ int res;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
+ if (!s)
+ return JS_EXCEPTION;
+ if (JS_IsUndefined(method))
+ return JS_GetPrototype(ctx, s->target);
+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return ret;
+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
+ JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
+ goto fail;
+ }
+ res = JS_IsExtensible(ctx, s->target);
+ if (res < 0) {
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ if (!res) {
+ /* check invariant */
+ proto1 = JS_GetPrototype(ctx, s->target);
+ if (JS_IsException(proto1)) {
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {
+ JS_FreeValue(ctx, proto1);
+ fail:
+ JS_FreeValue(ctx, ret);
+ return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
+ }
+ JS_FreeValue(ctx, proto1);
+ }
+ return ret;
+}
+
+static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
+ JSValueConst proto_val, BOOL throw_flag)
+{
+ JSProxyData *s;
+ JSValue method, ret, proto1;
+ JSValueConst args[2];
+ BOOL res;
+ int res2;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);
+ args[0] = s->target;
+ args[1] = proto_val;
+ ret = JS_CallFree(ctx, method, s->handler, 2, args);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+ if (!res) {
+ if (throw_flag) {
+ JS_ThrowTypeError(ctx, "proxy: bad prototype");
+ return -1;
+ } else {
+ return FALSE;
+ }
+ }
+ res2 = JS_IsExtensible(ctx, s->target);
+ if (res2 < 0)
+ return -1;
+ if (!res2) {
+ proto1 = JS_GetPrototype(ctx, s->target);
+ if (JS_IsException(proto1))
+ return -1;
+ if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {
+ JS_FreeValue(ctx, proto1);
+ JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
+ return -1;
+ }
+ JS_FreeValue(ctx, proto1);
+ }
+ return TRUE;
+}
+
+static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
+{
+ JSProxyData *s;
+ JSValue method, ret;
+ BOOL res;
+ int res2;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_IsExtensible(ctx, s->target);
+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+ res2 = JS_IsExtensible(ctx, s->target);
+ if (res2 < 0)
+ return res2;
+ if (res != res2) {
+ JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");
+ return -1;
+ }
+ return res;
+}
+
+static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
+{
+ JSProxyData *s;
+ JSValue method, ret;
+ BOOL res;
+ int res2;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_PreventExtensions(ctx, s->target);
+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+ if (res) {
+ res2 = JS_IsExtensible(ctx, s->target);
+ if (res2 < 0)
+ return res2;
+ if (res2) {
+ JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");
+ return -1;
+ }
+ }
+ return res;
+}
+
+static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
+{
+ JSProxyData *s;
+ JSValue method, ret1, atom_val;
+ int ret, res;
+ JSObject *p;
+ JSValueConst args[2];
+ BOOL res2;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_HasProperty(ctx, s->target, atom);
+ atom_val = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(atom_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = atom_val;
+ ret1 = JS_CallFree(ctx, method, s->handler, 2, args);
+ JS_FreeValue(ctx, atom_val);
+ if (JS_IsException(ret1))
+ return -1;
+ ret = JS_ToBoolFree(ctx, ret1);
+ if (!ret) {
+ JSPropertyDescriptor desc;
+ p = JS_VALUE_GET_OBJ(s->target);
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
+ if (res < 0)
+ return -1;
+ if (res) {
+ res2 = !(desc.flags & JS_PROP_CONFIGURABLE);
+ js_free_desc(ctx, &desc);
+ if (res2 || !p->extensible) {
+ JS_ThrowTypeError(ctx, "proxy: inconsistent has");
+ return -1;
+ }
+ }
+ }
+ return ret;
+}
+
+static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
+ JSValueConst receiver)
+{
+ JSProxyData *s;
+ JSValue method, ret, atom_val;
+ int res;
+ JSValueConst args[3];
+ JSPropertyDescriptor desc;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);
+ if (!s)
+ return JS_EXCEPTION;
+ /* Note: recursion is possible thru the prototype of s->target */
+ if (JS_IsUndefined(method))
+ return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
+ atom_val = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(atom_val)) {
+ JS_FreeValue(ctx, method);
+ return JS_EXCEPTION;
+ }
+ args[0] = s->target;
+ args[1] = atom_val;
+ args[2] = receiver;
+ ret = JS_CallFree(ctx, method, s->handler, 3, args);
+ JS_FreeValue(ctx, atom_val);
+ if (JS_IsException(ret))
+ return JS_EXCEPTION;
+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (res) {
+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
+ if (!js_same_value(ctx, desc.value, ret)) {
+ goto fail;
+ }
+ } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {
+ if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {
+ fail:
+ js_free_desc(ctx, &desc);
+ JS_FreeValue(ctx, ret);
+ return JS_ThrowTypeError(ctx, "proxy: inconsistent get");
+ }
+ }
+ js_free_desc(ctx, &desc);
+ }
+ return ret;
+}
+
+static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
+ JSValueConst value, JSValueConst receiver, int flags)
+{
+ JSProxyData *s;
+ JSValue method, ret1, atom_val;
+ int ret, res;
+ JSValueConst args[4];
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method)) {
+ return JS_SetPropertyGeneric(ctx, s->target, atom,
+ JS_DupValue(ctx, value), receiver,
+ flags);
+ }
+ atom_val = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(atom_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = atom_val;
+ args[2] = value;
+ args[3] = receiver;
+ ret1 = JS_CallFree(ctx, method, s->handler, 4, args);
+ JS_FreeValue(ctx, atom_val);
+ if (JS_IsException(ret1))
+ return -1;
+ ret = JS_ToBoolFree(ctx, ret1);
+ if (ret) {
+ JSPropertyDescriptor desc;
+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
+ if (res < 0)
+ return -1;
+ if (res) {
+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
+ if (!js_same_value(ctx, desc.value, value)) {
+ goto fail;
+ }
+ } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {
+ fail:
+ js_free_desc(ctx, &desc);
+ JS_ThrowTypeError(ctx, "proxy: inconsistent set");
+ return -1;
+ }
+ js_free_desc(ctx, &desc);
+ }
+ } else {
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ JS_ThrowTypeError(ctx, "proxy: cannot set property");
+ return -1;
+ }
+ }
+ return ret;
+}
+
+static JSValue js_create_desc(JSContext *ctx, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags)
+{
+ JSValue ret;
+ ret = JS_NewObject(ctx);
+ if (JS_IsException(ret))
+ return ret;
+ if (flags & JS_PROP_HAS_GET) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_SET) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_VALUE) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_WRITABLE) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
+ JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_ENUMERABLE) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
+ JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_CONFIGURABLE) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
+ JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0),
+ JS_PROP_C_W_E);
+ }
+ return ret;
+}
+
+static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,
+ JSValueConst obj, JSAtom prop)
+{
+ JSProxyData *s;
+ JSValue method, trap_result_obj, prop_val;
+ int res, target_desc_ret, ret;
+ JSObject *p;
+ JSValueConst args[2];
+ JSPropertyDescriptor result_desc, target_desc;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);
+ if (!s)
+ return -1;
+ p = JS_VALUE_GET_OBJ(s->target);
+ if (JS_IsUndefined(method)) {
+ return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);
+ }
+ prop_val = JS_AtomToValue(ctx, prop);
+ if (JS_IsException(prop_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = prop_val;
+ trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);
+ JS_FreeValue(ctx, prop_val);
+ if (JS_IsException(trap_result_obj))
+ return -1;
+ if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {
+ JS_FreeValue(ctx, trap_result_obj);
+ goto fail;
+ }
+ target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);
+ if (target_desc_ret < 0) {
+ JS_FreeValue(ctx, trap_result_obj);
+ return -1;
+ }
+ if (target_desc_ret)
+ js_free_desc(ctx, &target_desc);
+ if (JS_IsUndefined(trap_result_obj)) {
+ if (target_desc_ret) {
+ if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)
+ goto fail;
+ }
+ ret = FALSE;
+ } else {
+ int flags1, extensible_target;
+ extensible_target = JS_IsExtensible(ctx, s->target);
+ if (extensible_target < 0) {
+ JS_FreeValue(ctx, trap_result_obj);
+ return -1;
+ }
+ res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);
+ JS_FreeValue(ctx, trap_result_obj);
+ if (res < 0)
+ return -1;
+
+ if (target_desc_ret) {
+ /* convert result_desc.flags to defineProperty flags */
+ flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;
+ if (result_desc.flags & JS_PROP_GETSET)
+ flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;
+ else
+ flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;
+ /* XXX: not complete check: need to compare value &
+ getter/setter as in defineproperty */
+ if (!check_define_prop_flags(target_desc.flags, flags1))
+ goto fail1;
+ } else {
+ if (!extensible_target)
+ goto fail1;
+ }
+ if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {
+ if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))
+ goto fail1;
+ if ((result_desc.flags &
+ (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&
+ target_desc_ret &&
+ (target_desc.flags & JS_PROP_WRITABLE) != 0) {
+ /* proxy-missing-checks */
+ fail1:
+ js_free_desc(ctx, &result_desc);
+ fail:
+ JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");
+ return -1;
+ }
+ }
+ ret = TRUE;
+ if (pdesc) {
+ *pdesc = result_desc;
+ } else {
+ js_free_desc(ctx, &result_desc);
+ }
+ }
+ return ret;
+}
+
+static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags)
+{
+ JSProxyData *s;
+ JSValue method, ret1, prop_val, desc_val;
+ int res, ret;
+ JSObject *p;
+ JSValueConst args[3];
+ JSPropertyDescriptor desc;
+ BOOL setting_not_configurable;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method)) {
+ return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);
+ }
+ prop_val = JS_AtomToValue(ctx, prop);
+ if (JS_IsException(prop_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ desc_val = js_create_desc(ctx, val, getter, setter, flags);
+ if (JS_IsException(desc_val)) {
+ JS_FreeValue(ctx, prop_val);
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = prop_val;
+ args[2] = desc_val;
+ ret1 = JS_CallFree(ctx, method, s->handler, 3, args);
+ JS_FreeValue(ctx, prop_val);
+ JS_FreeValue(ctx, desc_val);
+ if (JS_IsException(ret1))
+ return -1;
+ ret = JS_ToBoolFree(ctx, ret1);
+ if (!ret) {
+ if (flags & JS_PROP_THROW) {
+ JS_ThrowTypeError(ctx, "proxy: defineProperty exception");
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ p = JS_VALUE_GET_OBJ(s->target);
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (res < 0)
+ return -1;
+ setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |
+ JS_PROP_CONFIGURABLE)) ==
+ JS_PROP_HAS_CONFIGURABLE);
+ if (!res) {
+ if (!p->extensible || setting_not_configurable)
+ goto fail;
+ } else {
+ if (!check_define_prop_flags(desc.flags, flags) ||
+ ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {
+ goto fail1;
+ }
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==
+ JS_PROP_GETSET) {
+ if ((flags & JS_PROP_HAS_GET) &&
+ !js_same_value(ctx, getter, desc.getter)) {
+ goto fail1;
+ }
+ if ((flags & JS_PROP_HAS_SET) &&
+ !js_same_value(ctx, setter, desc.setter)) {
+ goto fail1;
+ }
+ }
+ } else if (flags & JS_PROP_HAS_VALUE) {
+ if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==
+ JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {
+ /* missing-proxy-check feature */
+ goto fail1;
+ } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
+ !js_same_value(ctx, val, desc.value)) {
+ goto fail1;
+ }
+ }
+ if (flags & JS_PROP_HAS_WRITABLE) {
+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |
+ JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {
+ /* proxy-missing-checks */
+ fail1:
+ js_free_desc(ctx, &desc);
+ fail:
+ JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");
+ return -1;
+ }
+ }
+ js_free_desc(ctx, &desc);
+ }
+ return 1;
+}
+
+static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,
+ JSAtom atom)
+{
+ JSProxyData *s;
+ JSValue method, ret, atom_val;
+ int res, res2, is_extensible;
+ JSValueConst args[2];
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method)) {
+ return JS_DeleteProperty(ctx, s->target, atom, 0);
+ }
+ atom_val = JS_AtomToValue(ctx, atom);;
+ if (JS_IsException(atom_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = atom_val;
+ ret = JS_CallFree(ctx, method, s->handler, 2, args);
+ JS_FreeValue(ctx, atom_val);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+ if (res) {
+ JSPropertyDescriptor desc;
+ res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
+ if (res2 < 0)
+ return -1;
+ if (res2) {
+ if (!(desc.flags & JS_PROP_CONFIGURABLE))
+ goto fail;
+ is_extensible = JS_IsExtensible(ctx, s->target);
+ if (is_extensible < 0)
+ goto fail1;
+ if (!is_extensible) {
+ /* proxy-missing-checks */
+ fail:
+ JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");
+ fail1:
+ js_free_desc(ctx, &desc);
+ return -1;
+ }
+ js_free_desc(ctx, &desc);
+ }
+ }
+ return res;
+}
+
+/* return the index of the property or -1 if not found */
+static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)
+{
+ int i;
+ for(i = 0; i < n; i++) {
+ if (tab[i].atom == atom)
+ return i;
+ }
+ return -1;
+}
+
+static int js_proxy_get_own_property_names(JSContext *ctx,
+ JSPropertyEnum **ptab,
+ uint32_t *plen,
+ JSValueConst obj)
+{
+ JSProxyData *s;
+ JSValue method, prop_array, val;
+ uint32_t len, i, len2;
+ JSPropertyEnum *tab, *tab2;
+ JSAtom atom;
+ JSPropertyDescriptor desc;
+ int res, is_extensible, idx;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method)) {
+ return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
+ JS_VALUE_GET_OBJ(s->target),
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
+ }
+ prop_array = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(prop_array))
+ return -1;
+ tab = NULL;
+ len = 0;
+ tab2 = NULL;
+ len2 = 0;
+ if (js_get_length32(ctx, &len, prop_array))
+ goto fail;
+ if (len > 0) {
+ tab = js_mallocz(ctx, sizeof(tab[0]) * len);
+ if (!tab)
+ goto fail;
+ }
+ for(i = 0; i < len; i++) {
+ val = JS_GetPropertyUint32(ctx, prop_array, i);
+ if (JS_IsException(val))
+ goto fail;
+ if (!JS_IsString(val) && !JS_IsSymbol(val)) {
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");
+ goto fail;
+ }
+ atom = JS_ValueToAtom(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ tab[i].atom = atom;
+ tab[i].is_enumerable = FALSE; /* XXX: redundant? */
+ }
+
+ /* check duplicate properties (XXX: inefficient, could store the
+ * properties an a temporary object to use the hash) */
+ for(i = 1; i < len; i++) {
+ if (find_prop_key(tab, i, tab[i].atom) >= 0) {
+ JS_ThrowTypeError(ctx, "proxy: duplicate property");
+ goto fail;
+ }
+ }
+
+ is_extensible = JS_IsExtensible(ctx, s->target);
+ if (is_extensible < 0)
+ goto fail;
+
+ /* check if there are non configurable properties */
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ goto fail;
+ }
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
+ goto fail;
+ for(i = 0; i < len2; i++) {
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ goto fail;
+ }
+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),
+ tab2[i].atom);
+ if (res < 0)
+ goto fail;
+ if (res) { /* safety, property should be found */
+ js_free_desc(ctx, &desc);
+ if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {
+ idx = find_prop_key(tab, len, tab2[i].atom);
+ if (idx < 0) {
+ JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");
+ goto fail;
+ }
+ /* mark the property as found */
+ if (!is_extensible)
+ tab[idx].is_enumerable = TRUE;
+ }
+ }
+ }
+ if (!is_extensible) {
+ /* check that all property in 'tab' were checked */
+ for(i = 0; i < len; i++) {
+ if (!tab[i].is_enumerable) {
+ JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");
+ goto fail;
+ }
+ }
+ }
+
+ js_free_prop_enum(ctx, tab2, len2);
+ JS_FreeValue(ctx, prop_array);
+ *ptab = tab;
+ *plen = len;
+ return 0;
+ fail:
+ js_free_prop_enum(ctx, tab2, len2);
+ js_free_prop_enum(ctx, tab, len);
+ JS_FreeValue(ctx, prop_array);
+ return -1;
+}
+
+static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSProxyData *s;
+ JSValue method, arg_array, ret;
+ JSValueConst args[3];
+
+ s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);
+ if (!s)
+ return JS_EXCEPTION;
+ if (!JS_IsConstructor(ctx, s->target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (JS_IsUndefined(method))
+ return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
+ arg_array = js_create_array(ctx, argc, argv);
+ if (JS_IsException(arg_array)) {
+ ret = JS_EXCEPTION;
+ goto fail;
+ }
+ args[0] = s->target;
+ args[1] = arg_array;
+ args[2] = new_target;
+ ret = JS_Call(ctx, method, s->handler, 3, args);
+ if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
+ JS_FreeValue(ctx, ret);
+ ret = JS_ThrowTypeErrorNotAnObject(ctx);
+ }
+ fail:
+ JS_FreeValue(ctx, method);
+ JS_FreeValue(ctx, arg_array);
+ return ret;
+}
+
+static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSProxyData *s;
+ JSValue method, arg_array, ret;
+ JSValueConst args[3];
+
+ if (flags & JS_CALL_FLAG_CONSTRUCTOR)
+ return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);
+
+ s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);
+ if (!s)
+ return JS_EXCEPTION;
+ if (!s->is_func) {
+ JS_FreeValue(ctx, method);
+ return JS_ThrowTypeError(ctx, "not a function");
+ }
+ if (JS_IsUndefined(method))
+ return JS_Call(ctx, s->target, this_obj, argc, argv);
+ arg_array = js_create_array(ctx, argc, argv);
+ if (JS_IsException(arg_array)) {
+ ret = JS_EXCEPTION;
+ goto fail;
+ }
+ args[0] = s->target;
+ args[1] = this_obj;
+ args[2] = arg_array;
+ ret = JS_Call(ctx, method, s->handler, 3, args);
+ fail:
+ JS_FreeValue(ctx, method);
+ JS_FreeValue(ctx, arg_array);
+ return ret;
+}
+
+static int js_proxy_isArray(JSContext *ctx, JSValueConst obj)
+{
+ JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
+ if (!s)
+ return FALSE;
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ return -1;
+ }
+ return JS_IsArray(ctx, s->target);
+}
+
+static const JSClassExoticMethods js_proxy_exotic_methods = {
+ .get_own_property = js_proxy_get_own_property,
+ .define_own_property = js_proxy_define_own_property,
+ .delete_property = js_proxy_delete_property,
+ .get_own_property_names = js_proxy_get_own_property_names,
+ .has_property = js_proxy_has,
+ .get_property = js_proxy_get,
+ .set_property = js_proxy_set,
+};
+
+static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst target, handler;
+ JSValue obj;
+ JSProxyData *s;
+
+ target = argv[0];
+ handler = argv[1];
+ if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||
+ JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);
+ if (JS_IsException(obj))
+ return obj;
+ s = js_malloc(ctx, sizeof(JSProxyData));
+ if (!s) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ s->target = JS_DupValue(ctx, target);
+ s->handler = JS_DupValue(ctx, handler);
+ s->is_func = JS_IsFunction(ctx, target);
+ s->is_revoked = FALSE;
+ JS_SetOpaque(obj, s);
+ JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));
+ return obj;
+}
+
+static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic,
+ JSValue *func_data)
+{
+ JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);
+ if (s) {
+ /* We do not free the handler and target in case they are
+ referenced as constants in the C call stack */
+ s->is_revoked = TRUE;
+ JS_FreeValue(ctx, func_data[0]);
+ func_data[0] = JS_NULL;
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_proxy_revoke_constructor(JSContext *ctx,
+ JSValueConst proxy_obj)
+{
+ return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);
+}
+
+static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;
+
+ proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);
+ if (JS_IsException(proxy_obj))
+ goto fail;
+ revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);
+ if (JS_IsException(revoke_obj))
+ goto fail;
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ goto fail;
+ // XXX: exceptions?
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, proxy_obj);
+ JS_FreeValue(ctx, revoke_obj);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_proxy_funcs[] = {
+ JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),
+};
+
+static const JSClassShortDef js_proxy_class_def[] = {
+ { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */
+};
+
+void JS_AddIntrinsicProxy(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValue obj1;
+
+ if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {
+ init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,
+ countof(js_proxy_class_def));
+ rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;
+ rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;
+ }
+
+ obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,
+ JS_CFUNC_constructor, 0);
+ JS_SetConstructorBit(ctx, obj1, TRUE);
+ JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,
+ countof(js_proxy_funcs));
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",
+ obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+}
+
+/* Symbol */
+
+static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ JSString *p;
+
+ if (!JS_IsUndefined(new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ p = NULL;
+ } else {
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_STRING(str);
+ }
+ return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);
+}
+
+static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_SYMBOL) {
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a symbol");
+}
+
+static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ val = js_thisSymbolValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ /* XXX: use JS_ToStringInternal() with a flags */
+ ret = js_string_constructor(ctx, JS_UNDEFINED, 1, &val);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisSymbolValue(ctx, this_val);
+}
+
+static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)
+{
+ JSValue val, ret;
+ JSAtomStruct *p;
+
+ val = js_thisSymbolValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_PTR(val);
+ if (p->len == 0 && p->is_wide_char != 0) {
+ ret = JS_UNDEFINED;
+ } else {
+ ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static const JSCFunctionListEntry js_symbol_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_symbol_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),
+ // XXX: should have writable: false
+ JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),
+ JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),
+};
+
+static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return JS_EXCEPTION;
+ return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);
+}
+
+static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSAtomStruct *p;
+
+ if (!JS_IsSymbol(argv[0]))
+ return JS_ThrowTypeError(ctx, "not a symbol");
+ p = JS_VALUE_GET_PTR(argv[0]);
+ if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)
+ return JS_UNDEFINED;
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+}
+
+static const JSCFunctionListEntry js_symbol_funcs[] = {
+ JS_CFUNC_DEF("for", 1, js_symbol_for ),
+ JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),
+};
+
+/* Set/Map/WeakSet/WeakMap */
+
+typedef struct JSMapRecord {
+ int ref_count; /* used during enumeration to avoid freeing the record */
+ BOOL empty; /* TRUE if the record is deleted */
+ struct JSMapState *map;
+ struct JSMapRecord *next_weak_ref;
+ struct list_head link;
+ struct list_head hash_link;
+ JSValue key;
+ JSValue value;
+} JSMapRecord;
+
+typedef struct JSMapState {
+ BOOL is_weak; /* TRUE if WeakSet/WeakMap */
+ struct list_head records; /* list of JSMapRecord.link */
+ uint32_t record_count;
+ struct list_head *hash_table;
+ uint32_t hash_size; /* must be a power of two */
+ uint32_t record_count_threshold; /* count at which a hash table
+ resize is needed */
+} JSMapState;
+
+#define MAGIC_SET (1 << 0)
+#define MAGIC_WEAK (1 << 1)
+
+static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s;
+ JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
+ JSValueConst arr;
+ BOOL is_set, is_weak;
+
+ is_set = magic & MAGIC_SET;
+ is_weak = ((magic & MAGIC_WEAK) != 0);
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ goto fail;
+ init_list_head(&s->records);
+ s->is_weak = is_weak;
+ JS_SetOpaque(obj, s);
+ s->hash_size = 1;
+ s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
+ if (!s->hash_table)
+ goto fail;
+ init_list_head(&s->hash_table[0]);
+ s->record_count_threshold = 4;
+
+ arr = JS_UNDEFINED;
+ if (argc > 0)
+ arr = argv[0];
+ if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
+ JSValue item, ret;
+ BOOL done;
+
+ adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
+ if (JS_IsException(adder))
+ goto fail;
+ if (!JS_IsFunction(ctx, adder)) {
+ JS_ThrowTypeError(ctx, "set/add is not a function");
+ goto fail;
+ }
+
+ iter = JS_GetIterator(ctx, arr, FALSE);
+ if (JS_IsException(iter))
+ goto fail;
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail;
+
+ for(;;) {
+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(item))
+ goto fail;
+ if (done) {
+ JS_FreeValue(ctx, item);
+ break;
+ }
+ if (is_set) {
+ ret = JS_Call(ctx, adder, obj, 1, &item);
+ if (JS_IsException(ret)) {
+ JS_FreeValue(ctx, item);
+ goto fail;
+ }
+ } else {
+ JSValue key, value;
+ JSValueConst args[2];
+ key = JS_UNDEFINED;
+ value = JS_UNDEFINED;
+ if (!JS_IsObject(item)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto fail1;
+ }
+ key = JS_GetPropertyUint32(ctx, item, 0);
+ if (JS_IsException(key))
+ goto fail1;
+ value = JS_GetPropertyUint32(ctx, item, 1);
+ if (JS_IsException(value))
+ goto fail1;
+ args[0] = key;
+ args[1] = value;
+ ret = JS_Call(ctx, adder, obj, 2, args);
+ if (JS_IsException(ret)) {
+ fail1:
+ JS_FreeValue(ctx, item);
+ JS_FreeValue(ctx, key);
+ JS_FreeValue(ctx, value);
+ goto fail;
+ }
+ JS_FreeValue(ctx, key);
+ JS_FreeValue(ctx, value);
+ }
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, item);
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, adder);
+ }
+ return obj;
+ fail:
+ if (JS_IsObject(iter)) {
+ /* close the iterator object, preserving pending exception */
+ JS_IteratorClose(ctx, iter, TRUE);
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, adder);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+/* XXX: could normalize strings to speed up comparison */
+static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(key);
+ /* convert -0.0 to +0.0 */
+ if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
+ key = JS_NewInt32(ctx, 0);
+ }
+ return key;
+}
+
+/* XXX: better hash ? */
+static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
+{
+ uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
+ uint32_t h;
+ double d;
+ JSFloat64Union u;
+
+ switch(tag) {
+ case JS_TAG_BOOL:
+ h = JS_VALUE_GET_INT(key);
+ break;
+ case JS_TAG_STRING:
+ h = hash_string(JS_VALUE_GET_STRING(key), 0);
+ break;
+ case JS_TAG_OBJECT:
+ case JS_TAG_SYMBOL:
+ h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;
+ break;
+ case JS_TAG_INT:
+ d = JS_VALUE_GET_INT(key) * 3163;
+ goto hash_float64;
+ case JS_TAG_FLOAT64:
+ d = JS_VALUE_GET_FLOAT64(key);
+ /* normalize the NaN */
+ if (isnan(d))
+ d = JS_FLOAT64_NAN;
+ hash_float64:
+ u.d = d;
+ h = (u.u32[0] ^ u.u32[1]) * 3163;
+ break;
+ default:
+ h = 0; /* XXX: bignum support */
+ break;
+ }
+ h ^= tag;
+ return h;
+}
+
+static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
+ JSValueConst key)
+{
+ struct list_head *el;
+ JSMapRecord *mr;
+ uint32_t h;
+ h = map_hash_key(ctx, key) & (s->hash_size - 1);
+ list_for_each(el, &s->hash_table[h]) {
+ mr = list_entry(el, JSMapRecord, hash_link);
+ if (js_same_value_zero(ctx, mr->key, key))
+ return mr;
+ }
+ return NULL;
+}
+
+static void map_hash_resize(JSContext *ctx, JSMapState *s)
+{
+ uint32_t new_hash_size, i, h;
+ size_t slack;
+ struct list_head *new_hash_table, *el;
+ JSMapRecord *mr;
+
+ /* XXX: no reporting of memory allocation failure */
+ if (s->hash_size == 1)
+ new_hash_size = 4;
+ else
+ new_hash_size = s->hash_size * 2;
+ new_hash_table = js_realloc2(ctx, s->hash_table,
+ sizeof(new_hash_table[0]) * new_hash_size, &slack);
+ if (!new_hash_table)
+ return;
+ new_hash_size += slack / sizeof(*new_hash_table);
+
+ for(i = 0; i < new_hash_size; i++)
+ init_list_head(&new_hash_table[i]);
+
+ list_for_each(el, &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ if (!mr->empty) {
+ h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
+ list_add_tail(&mr->hash_link, &new_hash_table[h]);
+ }
+ }
+ s->hash_table = new_hash_table;
+ s->hash_size = new_hash_size;
+ s->record_count_threshold = new_hash_size * 2;
+}
+
+static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
+ JSValueConst key)
+{
+ uint32_t h;
+ JSMapRecord *mr;
+
+ mr = js_malloc(ctx, sizeof(*mr));
+ if (!mr)
+ return NULL;
+ mr->ref_count = 1;
+ mr->map = s;
+ mr->empty = FALSE;
+ if (s->is_weak) {
+ JSObject *p = JS_VALUE_GET_OBJ(key);
+ /* Add the weak reference */
+ mr->next_weak_ref = p->first_weak_ref;
+ p->first_weak_ref = mr;
+ } else {
+ JS_DupValue(ctx, key);
+ }
+ mr->key = key;
+ h = map_hash_key(ctx, key) & (s->hash_size - 1);
+ list_add_tail(&mr->hash_link, &s->hash_table[h]);
+ list_add_tail(&mr->link, &s->records);
+ s->record_count++;
+ if (s->record_count >= s->record_count_threshold) {
+ map_hash_resize(ctx, s);
+ }
+ return mr;
+}
+
+/* Remove the weak reference from the object weak
+ reference list. we don't use a doubly linked list to
+ save space, assuming a given object has few weak
+ references to it */
+static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
+{
+ JSMapRecord **pmr, *mr1;
+ JSObject *p;
+
+ p = JS_VALUE_GET_OBJ(mr->key);
+ pmr = &p->first_weak_ref;
+ for(;;) {
+ mr1 = *pmr;
+ assert(mr1 != NULL);
+ if (mr1 == mr)
+ break;
+ pmr = &mr1->next_weak_ref;
+ }
+ *pmr = mr1->next_weak_ref;
+}
+
+static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
+{
+ if (mr->empty)
+ return;
+ list_del(&mr->hash_link);
+ if (s->is_weak) {
+ delete_weak_ref(rt, mr);
+ } else {
+ JS_FreeValueRT(rt, mr->key);
+ }
+ JS_FreeValueRT(rt, mr->value);
+ if (--mr->ref_count == 0) {
+ list_del(&mr->link);
+ js_free_rt(rt, mr);
+ } else {
+ /* keep a zombie record for iterators */
+ mr->empty = TRUE;
+ mr->key = JS_UNDEFINED;
+ mr->value = JS_UNDEFINED;
+ }
+ s->record_count--;
+}
+
+static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
+{
+ if (--mr->ref_count == 0) {
+ /* the record can be safely removed */
+ assert(mr->empty);
+ list_del(&mr->link);
+ js_free_rt(rt, mr);
+ }
+}
+
+static void reset_weak_ref(JSRuntime *rt, JSObject *p)
+{
+ JSMapRecord *mr, *mr_next;
+ JSMapState *s;
+
+ /* first pass to remove the records from the WeakMap/WeakSet
+ lists */
+ for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
+ s = mr->map;
+ assert(s->is_weak);
+ assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
+ list_del(&mr->hash_link);
+ list_del(&mr->link);
+ }
+
+ /* second pass to free the values to avoid modifying the weak
+ reference list while traversing it. */
+ for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {
+ mr_next = mr->next_weak_ref;
+ JS_FreeValueRT(rt, mr->value);
+ js_free_rt(rt, mr);
+ }
+
+ p->first_weak_ref = NULL; /* fail safe */
+}
+
+static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSMapRecord *mr;
+ JSValueConst key, value;
+
+ if (!s)
+ return JS_EXCEPTION;
+ key = map_normalize_key(ctx, argv[0]);
+ if (s->is_weak && !JS_IsObject(key))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ if (magic & MAGIC_SET)
+ value = JS_UNDEFINED;
+ else
+ value = argv[1];
+ mr = map_find_record(ctx, s, key);
+ if (mr) {
+ JS_FreeValue(ctx, mr->value);
+ } else {
+ mr = map_add_record(ctx, s, key);
+ if (!mr)
+ return JS_EXCEPTION;
+ }
+ mr->value = JS_DupValue(ctx, value);
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSMapRecord *mr;
+ JSValueConst key;
+
+ if (!s)
+ return JS_EXCEPTION;
+ key = map_normalize_key(ctx, argv[0]);
+ mr = map_find_record(ctx, s, key);
+ if (!mr)
+ return JS_UNDEFINED;
+ else
+ return JS_DupValue(ctx, mr->value);
+}
+
+static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSMapRecord *mr;
+ JSValueConst key;
+
+ if (!s)
+ return JS_EXCEPTION;
+ key = map_normalize_key(ctx, argv[0]);
+ mr = map_find_record(ctx, s, key);
+ return JS_NewBool(ctx, (mr != NULL));
+}
+
+static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSMapRecord *mr;
+ JSValueConst key;
+
+ if (!s)
+ return JS_EXCEPTION;
+ key = map_normalize_key(ctx, argv[0]);
+ mr = map_find_record(ctx, s, key);
+ if (!mr)
+ return JS_FALSE;
+ map_delete_record(ctx->rt, s, mr);
+ return JS_TRUE;
+}
+
+static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ struct list_head *el, *el1;
+ JSMapRecord *mr;
+
+ if (!s)
+ return JS_EXCEPTION;
+ list_for_each_safe(el, el1, &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ map_delete_record(ctx->rt, s, mr);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ if (!s)
+ return JS_EXCEPTION;
+ return JS_NewUint32(ctx, s->record_count);
+}
+
+static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSValueConst func, this_arg;
+ JSValue ret, args[3];
+ struct list_head *el;
+ JSMapRecord *mr;
+
+ if (!s)
+ return JS_EXCEPTION;
+ func = argv[0];
+ if (argc > 1)
+ this_arg = argv[1];
+ else
+ this_arg = JS_UNDEFINED;
+ if (check_function(ctx, func))
+ return JS_EXCEPTION;
+ /* Note: the list can be modified while traversing it, but the
+ current element is locked */
+ el = s->records.next;
+ while (el != &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ if (!mr->empty) {
+ mr->ref_count++;
+ /* must duplicate in case the record is deleted */
+ args[1] = JS_DupValue(ctx, mr->key);
+ if (magic)
+ args[0] = args[1];
+ else
+ args[0] = JS_DupValue(ctx, mr->value);
+ args[2] = this_val;
+ ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
+ JS_FreeValue(ctx, args[0]);
+ if (!magic)
+ JS_FreeValue(ctx, args[1]);
+ el = el->next;
+ map_decref_record(ctx->rt, mr);
+ if (JS_IsException(ret))
+ return ret;
+ JS_FreeValue(ctx, ret);
+ } else {
+ el = el->next;
+ }
+ }
+ return JS_UNDEFINED;
+}
+
+static void js_map_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p;
+ JSMapState *s;
+ struct list_head *el, *el1;
+ JSMapRecord *mr;
+
+ p = JS_VALUE_GET_OBJ(val);
+ s = p->u.map_state;
+ if (s) {
+ /* if the object is deleted we are sure that no iterator is
+ using it */
+ list_for_each_safe(el, el1, &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ if (!mr->empty) {
+ if (s->is_weak)
+ delete_weak_ref(rt, mr);
+ else
+ JS_FreeValueRT(rt, mr->key);
+ JS_FreeValueRT(rt, mr->value);
+ }
+ js_free_rt(rt, mr);
+ }
+ js_free_rt(rt, s->hash_table);
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSMapState *s;
+ struct list_head *el;
+ JSMapRecord *mr;
+
+ s = p->u.map_state;
+ if (s) {
+ list_for_each(el, &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ if (!s->is_weak)
+ JS_MarkValue(rt, mr->key, mark_func);
+ JS_MarkValue(rt, mr->value, mark_func);
+ }
+ }
+}
+
+/* Map Iterator */
+
+typedef struct JSMapIteratorData {
+ JSValue obj;
+ JSIteratorKindEnum kind;
+ JSMapRecord *cur_record;
+} JSMapIteratorData;
+
+static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p;
+ JSMapIteratorData *it;
+
+ p = JS_VALUE_GET_OBJ(val);
+ it = p->u.map_iterator_data;
+ if (it) {
+ /* During the GC sweep phase the Map finalizer may be
+ called before the Map iterator finalizer */
+ if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
+ map_decref_record(rt, it->cur_record);
+ }
+ JS_FreeValueRT(rt, it->obj);
+ js_free_rt(rt, it);
+ }
+}
+
+static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSMapIteratorData *it;
+ it = p->u.map_iterator_data;
+ if (it) {
+ /* the record is already marked by the object */
+ JS_MarkValue(rt, it->obj, mark_func);
+ }
+}
+
+static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSIteratorKindEnum kind;
+ JSMapState *s;
+ JSMapIteratorData *it;
+ JSValue enum_obj;
+
+ kind = magic >> 2;
+ magic &= 3;
+ s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ if (!s)
+ return JS_EXCEPTION;
+ enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
+ if (JS_IsException(enum_obj))
+ goto fail;
+ it = js_malloc(ctx, sizeof(*it));
+ if (!it) {
+ JS_FreeValue(ctx, enum_obj);
+ goto fail;
+ }
+ it->obj = JS_DupValue(ctx, this_val);
+ it->kind = kind;
+ it->cur_record = NULL;
+ JS_SetOpaque(enum_obj, it);
+ return enum_obj;
+ fail:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSMapIteratorData *it;
+ JSMapState *s;
+ JSMapRecord *mr;
+ struct list_head *el;
+
+ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
+ if (!it) {
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+ }
+ if (JS_IsUndefined(it->obj))
+ goto done;
+ s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
+ assert(s != NULL);
+ if (!it->cur_record) {
+ el = s->records.next;
+ } else {
+ mr = it->cur_record;
+ el = mr->link.next;
+ map_decref_record(ctx->rt, mr); /* the record can be freed here */
+ }
+ for(;;) {
+ if (el == &s->records) {
+ /* no more record */
+ it->cur_record = NULL;
+ JS_FreeValue(ctx, it->obj);
+ it->obj = JS_UNDEFINED;
+ done:
+ /* end of enumeration */
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ }
+ mr = list_entry(el, JSMapRecord, link);
+ if (!mr->empty)
+ break;
+ /* get the next record */
+ el = mr->link.next;
+ }
+
+ /* lock the record so that it won't be freed */
+ mr->ref_count++;
+ it->cur_record = mr;
+ *pdone = FALSE;
+
+ if (it->kind == JS_ITERATOR_KIND_KEY) {
+ return JS_DupValue(ctx, mr->key);
+ } else {
+ JSValueConst args[2];
+ args[0] = mr->key;
+ if (magic)
+ args[1] = mr->key;
+ else
+ args[1] = mr->value;
+ if (it->kind == JS_ITERATOR_KIND_VALUE) {
+ return JS_DupValue(ctx, args[1]);
+ } else {
+ return js_create_array(ctx, 2, args);
+ }
+ }
+}
+
+static const JSCFunctionListEntry js_map_funcs[] = {
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+};
+
+static const JSCFunctionListEntry js_map_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
+ JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
+ JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
+ JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
+ JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
+ JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_set_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
+ JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
+ JS_ALIAS_DEF("keys", "values" ),
+ JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
+ js_map_proto_funcs,
+ js_set_proto_funcs,
+ js_weak_map_proto_funcs,
+ js_weak_set_proto_funcs,
+ js_map_iterator_proto_funcs,
+ js_set_iterator_proto_funcs,
+};
+
+static const uint8_t js_map_proto_funcs_count[6] = {
+ countof(js_map_proto_funcs),
+ countof(js_set_proto_funcs),
+ countof(js_weak_map_proto_funcs),
+ countof(js_weak_set_proto_funcs),
+ countof(js_map_iterator_proto_funcs),
+ countof(js_set_iterator_proto_funcs),
+};
+
+void JS_AddIntrinsicMapSet(JSContext *ctx)
+{
+ int i;
+ JSValue obj1;
+ char buf[ATOM_GET_STR_BUF_SIZE];
+
+ for(i = 0; i < 4; i++) {
+ const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
+ JS_ATOM_Map + i);
+ ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],
+ js_map_proto_funcs_ptr[i],
+ js_map_proto_funcs_count[i]);
+ obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,
+ JS_CFUNC_constructor_magic, i);
+ if (i < 2) {
+ JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,
+ countof(js_map_funcs));
+ }
+ JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);
+ }
+
+ for(i = 0; i < 2; i++) {
+ ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
+ JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],
+ js_map_proto_funcs_ptr[i + 4],
+ js_map_proto_funcs_count[i + 4]);
+ }
+}
+
+/* Generator */
+static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
+};
+
+static const JSCFunctionListEntry js_generator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
+ JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
+ JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
+};
+
+/* Promise */
+
+typedef enum JSPromiseStateEnum {
+ JS_PROMISE_PENDING,
+ JS_PROMISE_FULFILLED,
+ JS_PROMISE_REJECTED,
+} JSPromiseStateEnum;
+
+typedef struct JSPromiseData {
+ JSPromiseStateEnum promise_state;
+ /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
+ struct list_head promise_reactions[2];
+ BOOL is_handled; /* Note: only useful to debug */
+ JSValue promise_result;
+} JSPromiseData;
+
+typedef struct JSPromiseFunctionDataResolved {
+ int ref_count;
+ BOOL already_resolved;
+} JSPromiseFunctionDataResolved;
+
+typedef struct JSPromiseFunctionData {
+ JSValue promise;
+ JSPromiseFunctionDataResolved *presolved;
+} JSPromiseFunctionData;
+
+typedef struct JSPromiseReactionData {
+ struct list_head link; /* not used in promise_reaction_job */
+ JSValue resolving_funcs[2];
+ JSValue handler;
+} JSPromiseReactionData;
+
+static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
+ JSValueConst promise);
+
+static void promise_reaction_data_free(JSRuntime *rt,
+ JSPromiseReactionData *rd)
+{
+ JS_FreeValueRT(rt, rd->resolving_funcs[0]);
+ JS_FreeValueRT(rt, rd->resolving_funcs[1]);
+ JS_FreeValueRT(rt, rd->handler);
+ js_free_rt(rt, rd);
+}
+
+static JSValue promise_reaction_job(JSContext *ctx, int argc,
+ JSValueConst *argv)
+{
+ JSValueConst handler, arg, func;
+ JSValue res, res2;
+ BOOL is_reject;
+
+ assert(argc == 5);
+ handler = argv[2];
+ is_reject = JS_ToBool(ctx, argv[3]);
+ arg = argv[4];
+#ifdef DUMP_PROMISE
+ printf("promise_reaction_job: is_reject=%d\n", is_reject);
+#endif
+
+ if (JS_IsUndefined(handler)) {
+ if (is_reject) {
+ res = JS_Throw(ctx, JS_DupValue(ctx, arg));
+ } else {
+ res = JS_DupValue(ctx, arg);
+ }
+ } else {
+ res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);
+ }
+ is_reject = JS_IsException(res);
+ if (is_reject)
+ res = JS_GetException(ctx);
+ func = argv[is_reject];
+ /* as an extension, we support undefined as value to avoid
+ creating a dummy promise in the 'await' implementation of async
+ functions */
+ if (!JS_IsUndefined(func)) {
+ res2 = JS_Call(ctx, func, JS_UNDEFINED,
+ 1, &res);
+ } else {
+ res2 = JS_UNDEFINED;
+ }
+ JS_FreeValue(ctx, res);
+
+ return res2;
+}
+
+void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,
+ JSHostPromiseRejectionTracker *cb,
+ void *opaque)
+{
+ rt->host_promise_rejection_tracker = cb;
+ rt->host_promise_rejection_tracker_opaque = opaque;
+}
+
+static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
+ JSValueConst value, BOOL is_reject)
+{
+ JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
+ struct list_head *el, *el1;
+ JSPromiseReactionData *rd;
+ JSValueConst args[5];
+
+ if (!s || s->promise_state != JS_PROMISE_PENDING)
+ return; /* should never happen */
+ set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));
+ s->promise_state = JS_PROMISE_FULFILLED + is_reject;
+#ifdef DUMP_PROMISE
+ printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);
+#endif
+ if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
+ JSRuntime *rt = ctx->rt;
+ if (rt->host_promise_rejection_tracker) {
+ rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,
+ rt->host_promise_rejection_tracker_opaque);
+ }
+ }
+
+ list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
+ rd = list_entry(el, JSPromiseReactionData, link);
+ args[0] = rd->resolving_funcs[0];
+ args[1] = rd->resolving_funcs[1];
+ args[2] = rd->handler;
+ args[3] = JS_NewBool(ctx, is_reject);
+ args[4] = value;
+ JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
+ list_del(&rd->link);
+ promise_reaction_data_free(ctx->rt, rd);
+ }
+
+ list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {
+ rd = list_entry(el, JSPromiseReactionData, link);
+ list_del(&rd->link);
+ promise_reaction_data_free(ctx->rt, rd);
+ }
+}
+
+static void reject_promise(JSContext *ctx, JSValueConst promise,
+ JSValueConst value)
+{
+ fulfill_or_reject_promise(ctx, promise, value, TRUE);
+}
+
+static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst promise, thenable, then;
+ JSValue args[2], res;
+
+#ifdef DUMP_PROMISE
+ printf("js_promise_resolve_thenable_job\n");
+#endif
+ assert(argc == 3);
+ promise = argv[0];
+ thenable = argv[1];
+ then = argv[2];
+ if (js_create_resolving_functions(ctx, args, promise) < 0)
+ return JS_EXCEPTION;
+ res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
+ if (JS_IsException(res)) {
+ JSValue error = JS_GetException(ctx);
+ res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ }
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ return res;
+}
+
+static void js_promise_resolve_function_free_resolved(JSRuntime *rt,
+ JSPromiseFunctionDataResolved *sr)
+{
+ if (--sr->ref_count == 0) {
+ js_free_rt(rt, sr);
+ }
+}
+
+static int js_create_resolving_functions(JSContext *ctx,
+ JSValue *resolving_funcs,
+ JSValueConst promise)
+
+{
+ JSValue obj;
+ JSPromiseFunctionData *s;
+ JSPromiseFunctionDataResolved *sr;
+ int i, ret;
+
+ sr = js_malloc(ctx, sizeof(*sr));
+ if (!sr)
+ return -1;
+ sr->ref_count = 1;
+ sr->already_resolved = FALSE; /* must be shared between the two functions */
+ ret = 0;
+ for(i = 0; i < 2; i++) {
+ obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);
+ if (JS_IsException(obj))
+ goto fail;
+ s = js_malloc(ctx, sizeof(*s));
+ if (!s) {
+ JS_FreeValue(ctx, obj);
+ fail:
+
+ if (i != 0)
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ ret = -1;
+ break;
+ }
+ sr->ref_count++;
+ s->presolved = sr;
+ s->promise = JS_DupValue(ctx, promise);
+ JS_SetOpaque(obj, s);
+ js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);
+ resolving_funcs[i] = obj;
+ }
+ js_promise_resolve_function_free_resolved(ctx->rt, sr);
+ return ret;
+}
+
+static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
+ if (s) {
+ js_promise_resolve_function_free_resolved(rt, s->presolved);
+ JS_FreeValueRT(rt, s->promise);
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
+ if (s) {
+ JS_MarkValue(rt, s->promise, mark_func);
+ }
+}
+
+static JSValue js_promise_resolve_function_call(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int flags)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(func_obj);
+ JSPromiseFunctionData *s;
+ JSValueConst resolution, args[3];
+ JSValue then;
+ BOOL is_reject;
+
+ s = p->u.promise_function_data;
+ if (!s || s->presolved->already_resolved)
+ return JS_UNDEFINED;
+ s->presolved->already_resolved = TRUE;
+ is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;
+ if (argc > 0)
+ resolution = argv[0];
+ else
+ resolution = JS_UNDEFINED;
+#ifdef DUMP_PROMISE
+ printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);
+ JS_DumpValue(ctx, resolution);
+ printf("\n");
+#endif
+ if (is_reject || !JS_IsObject(resolution)) {
+ goto done;
+ } else if (js_same_value(ctx, resolution, s->promise)) {
+ JS_ThrowTypeError(ctx, "promise self resolution");
+ goto fail_reject;
+ }
+ then = JS_GetProperty(ctx, resolution, JS_ATOM_then);
+ if (JS_IsException(then)) {
+ JSValue error;
+ fail_reject:
+ error = JS_GetException(ctx);
+ reject_promise(ctx, s->promise, error);
+ JS_FreeValue(ctx, error);
+ } else if (!JS_IsFunction(ctx, then)) {
+ JS_FreeValue(ctx, then);
+ done:
+ fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);
+ } else {
+ args[0] = s->promise;
+ args[1] = resolution;
+ args[2] = then;
+ JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);
+ JS_FreeValue(ctx, then);
+ }
+ return JS_UNDEFINED;
+}
+
+static void js_promise_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
+ struct list_head *el, *el1;
+ int i;
+
+ if (!s)
+ return;
+ for(i = 0; i < 2; i++) {
+ list_for_each_safe(el, el1, &s->promise_reactions[i]) {
+ JSPromiseReactionData *rd =
+ list_entry(el, JSPromiseReactionData, link);
+ promise_reaction_data_free(rt, rd);
+ }
+ }
+ JS_FreeValueRT(rt, s->promise_result);
+ js_free_rt(rt, s);
+}
+
+static void js_promise_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
+ struct list_head *el;
+ int i;
+
+ if (!s)
+ return;
+ for(i = 0; i < 2; i++) {
+ list_for_each(el, &s->promise_reactions[i]) {
+ JSPromiseReactionData *rd =
+ list_entry(el, JSPromiseReactionData, link);
+ JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);
+ JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);
+ JS_MarkValue(rt, rd->handler, mark_func);
+ }
+ }
+ JS_MarkValue(rt, s->promise_result, mark_func);
+}
+
+static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst executor;
+ JSValue obj;
+ JSPromiseData *s;
+ JSValue args[2], ret;
+ int i;
+
+ executor = argv[0];
+ if (check_function(ctx, executor))
+ return JS_EXCEPTION;
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ goto fail;
+ s->promise_state = JS_PROMISE_PENDING;
+ s->is_handled = FALSE;
+ for(i = 0; i < 2; i++)
+ init_list_head(&s->promise_reactions[i]);
+ s->promise_result = JS_UNDEFINED;
+ JS_SetOpaque(obj, s);
+ if (js_create_resolving_functions(ctx, args, obj))
+ goto fail;
+ ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);
+ if (JS_IsException(ret)) {
+ JSValue ret2, error;
+ error = JS_GetException(ctx);
+ ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret2))
+ goto fail1;
+ JS_FreeValue(ctx, ret2);
+ }
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ return obj;
+ fail1:
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_promise_executor(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ int i;
+
+ for(i = 0; i < 2; i++) {
+ if (!JS_IsUndefined(func_data[i]))
+ return JS_ThrowTypeError(ctx, "resolving function already set");
+ func_data[i] = JS_DupValue(ctx, argv[i]);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_promise_executor_new(JSContext *ctx)
+{
+ JSValueConst func_data[2];
+
+ func_data[0] = JS_UNDEFINED;
+ func_data[1] = JS_UNDEFINED;
+ return JS_NewCFunctionData(ctx, js_promise_executor, 2,
+ 0, 2, func_data);
+}
+
+static JSValue js_new_promise_capability(JSContext *ctx,
+ JSValue *resolving_funcs,
+ JSValueConst ctor)
+{
+ JSValue executor, result_promise;
+ JSCFunctionDataRecord *s;
+ int i;
+
+ executor = js_promise_executor_new(ctx);
+ if (JS_IsException(executor))
+ return executor;
+
+ if (JS_IsUndefined(ctor)) {
+ result_promise = js_promise_constructor(ctx, ctor, 1,
+ &executor);
+ } else {
+ result_promise = JS_CallConstructor(ctx, ctor, 1,
+ &executor);
+ }
+ if (JS_IsException(result_promise))
+ goto fail;
+ s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);
+ for(i = 0; i < 2; i++) {
+ if (check_function(ctx, s->data[i]))
+ goto fail;
+ }
+ for(i = 0; i < 2; i++)
+ resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);
+ JS_FreeValue(ctx, executor);
+ return result_promise;
+ fail:
+ JS_FreeValue(ctx, executor);
+ JS_FreeValue(ctx, result_promise);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)
+{
+ return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);
+}
+
+static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue result_promise, resolving_funcs[2], ret;
+ BOOL is_reject = magic;
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {
+ JSValue ctor;
+ BOOL is_same;
+ ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);
+ if (JS_IsException(ctor))
+ return ctor;
+ is_same = js_same_value(ctx, ctor, this_val);
+ JS_FreeValue(ctx, ctor);
+ if (is_same)
+ return JS_DupValue(ctx, argv[0]);
+ }
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ if (JS_IsException(ret)) {
+ JS_FreeValue(ctx, result_promise);
+ return ret;
+ }
+ JS_FreeValue(ctx, ret);
+ return result_promise;
+}
+
+#if 0
+static JSValue js_promise___newPromiseCapability(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue result_promise, resolving_funcs[2], obj;
+ JSValueConst ctor;
+ ctor = argv[0];
+ if (!JS_IsObject(ctor))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj)) {
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ JS_FreeValue(ctx, result_promise);
+ return JS_EXCEPTION;
+ }
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);
+ return obj;
+}
+#endif
+
+static warn_unused int remainingElementsCount_add(JSContext *ctx,
+ JSValueConst resolve_element_env,
+ int addend)
+{
+ JSValue val;
+ int remainingElementsCount;
+
+ val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);
+ if (JS_IsException(val))
+ return -1;
+ if (JS_ToInt32Free(ctx, &remainingElementsCount, val))
+ return -1;
+ remainingElementsCount += addend;
+ if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,
+ JS_NewInt32(ctx, remainingElementsCount)) < 0)
+ return -1;
+ return (remainingElementsCount == 0);
+}
+
+#define PROMISE_MAGIC_all 0
+#define PROMISE_MAGIC_allSettled 1
+#define PROMISE_MAGIC_any 2
+
+static JSValue js_promise_all_resolve_element(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic,
+ JSValue *func_data)
+{
+ int resolve_type = magic & 3;
+ int is_reject = magic & 4;
+ BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);
+ JSValueConst values = func_data[2];
+ JSValueConst resolve = func_data[3];
+ JSValueConst resolve_element_env = func_data[4];
+ JSValue ret, obj;
+ int is_zero, index;
+
+ if (JS_ToInt32(ctx, &index, func_data[1]))
+ return JS_EXCEPTION;
+ if (alreadyCalled)
+ return JS_UNDEFINED;
+ func_data[0] = JS_NewBool(ctx, TRUE);
+
+ if (resolve_type == PROMISE_MAGIC_allSettled) {
+ JSValue str;
+
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");
+ if (JS_IsException(str))
+ goto fail1;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,
+ str,
+ JS_PROP_C_W_E) < 0)
+ goto fail1;
+ if (JS_DefinePropertyValue(ctx, obj,
+ is_reject ? JS_ATOM_reason : JS_ATOM_value,
+ JS_DupValue(ctx, argv[0]),
+ JS_PROP_C_W_E) < 0) {
+ fail1:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ } else {
+ obj = JS_DupValue(ctx, argv[0]);
+ }
+ if (JS_DefinePropertyValueUint32(ctx, values, index,
+ obj, JS_PROP_C_W_E) < 0)
+ return JS_EXCEPTION;
+
+ is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
+ if (is_zero < 0)
+ return JS_EXCEPTION;
+ if (is_zero) {
+ if (resolve_type == PROMISE_MAGIC_any) {
+ JSValue error;
+ error = js_aggregate_error_constructor(ctx, values);
+ if (JS_IsException(error))
+ return JS_EXCEPTION;
+ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ } else {
+ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &values);
+ }
+ if (JS_IsException(ret))
+ return ret;
+ JS_FreeValue(ctx, ret);
+ }
+ return JS_UNDEFINED;
+}
+
+/* magic = 0: Promise.all 1: Promise.allSettled */
+static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
+ JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
+ JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
+ JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
+ JSValueConst then_args[2], resolve_element_data[5];
+ BOOL done;
+ int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
+ if (JS_IsException(promise_resolve) ||
+ check_function(ctx, promise_resolve))
+ goto fail_reject;
+ iter = JS_GetIterator(ctx, argv[0], FALSE);
+ if (JS_IsException(iter)) {
+ JSValue error;
+ fail_reject:
+ error = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
+ &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret))
+ goto fail;
+ JS_FreeValue(ctx, ret);
+ } else {
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail_reject;
+ values = JS_NewArray(ctx);
+ if (JS_IsException(values))
+ goto fail_reject;
+ resolve_element_env = JS_NewArray(ctx);
+ if (JS_IsException(resolve_element_env))
+ goto fail_reject;
+ /* remainingElementsCount field */
+ if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,
+ JS_NewInt32(ctx, 1),
+ JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
+ goto fail_reject;
+
+ index = 0;
+ for(;;) {
+ /* XXX: conformance: should close the iterator if error on 'done'
+ access, but not on 'value' access */
+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(item))
+ goto fail_reject;
+ if (done)
+ break;
+ next_promise = JS_Call(ctx, promise_resolve,
+ this_val, 1, &item);
+ JS_FreeValue(ctx, item);
+ if (JS_IsException(next_promise)) {
+ fail_reject1:
+ JS_IteratorClose(ctx, iter, TRUE);
+ goto fail_reject;
+ }
+ resolve_element_data[0] = JS_NewBool(ctx, FALSE);
+ resolve_element_data[1] = JS_NewInt32(ctx, index);
+ resolve_element_data[2] = values;
+ resolve_element_data[3] = resolving_funcs[is_promise_any];
+ resolve_element_data[4] = resolve_element_env;
+ resolve_element =
+ JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
+ magic, 5, resolve_element_data);
+ if (JS_IsException(resolve_element)) {
+ JS_FreeValue(ctx, next_promise);
+ goto fail_reject1;
+ }
+
+ if (magic == PROMISE_MAGIC_allSettled) {
+ reject_element =
+ JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
+ magic | 4, 5, resolve_element_data);
+ if (JS_IsException(reject_element)) {
+ JS_FreeValue(ctx, next_promise);
+ goto fail_reject1;
+ }
+ } else if (magic == PROMISE_MAGIC_any) {
+ if (JS_DefinePropertyValueUint32(ctx, values, index,
+ JS_UNDEFINED, JS_PROP_C_W_E) < 0)
+ goto fail_reject1;
+ reject_element = resolve_element;
+ resolve_element = JS_DupValue(ctx, resolving_funcs[0]);
+ } else {
+ reject_element = JS_DupValue(ctx, resolving_funcs[1]);
+ }
+
+ if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {
+ JS_FreeValue(ctx, next_promise);
+ JS_FreeValue(ctx, resolve_element);
+ JS_FreeValue(ctx, reject_element);
+ goto fail_reject1;
+ }
+
+ then_args[0] = resolve_element;
+ then_args[1] = reject_element;
+ ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);
+ JS_FreeValue(ctx, resolve_element);
+ JS_FreeValue(ctx, reject_element);
+ if (check_exception_free(ctx, ret))
+ goto fail_reject1;
+ index++;
+ }
+
+ is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
+ if (is_zero < 0)
+ goto fail_reject;
+ if (is_zero) {
+ if (magic == PROMISE_MAGIC_any) {
+ JSValue error;
+ error = js_aggregate_error_constructor(ctx, values);
+ if (JS_IsException(error))
+ goto fail_reject;
+ JS_FreeValue(ctx, values);
+ values = error;
+ }
+ ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
+ 1, &values);
+ if (check_exception_free(ctx, ret))
+ goto fail_reject;
+ }
+ }
+ done:
+ JS_FreeValue(ctx, promise_resolve);
+ JS_FreeValue(ctx, resolve_element_env);
+ JS_FreeValue(ctx, values);
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return result_promise;
+ fail:
+ JS_FreeValue(ctx, result_promise);
+ result_promise = JS_EXCEPTION;
+ goto done;
+}
+
+static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
+ JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
+ JSValue promise_resolve = JS_UNDEFINED;
+ BOOL done;
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
+ if (JS_IsException(promise_resolve) ||
+ check_function(ctx, promise_resolve))
+ goto fail_reject;
+ iter = JS_GetIterator(ctx, argv[0], FALSE);
+ if (JS_IsException(iter)) {
+ JSValue error;
+ fail_reject:
+ error = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
+ &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret))
+ goto fail;
+ JS_FreeValue(ctx, ret);
+ } else {
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail_reject;
+
+ for(;;) {
+ /* XXX: conformance: should close the iterator if error on 'done'
+ access, but not on 'value' access */
+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(item))
+ goto fail_reject;
+ if (done)
+ break;
+ next_promise = JS_Call(ctx, promise_resolve,
+ this_val, 1, &item);
+ JS_FreeValue(ctx, item);
+ if (JS_IsException(next_promise)) {
+ fail_reject1:
+ JS_IteratorClose(ctx, iter, TRUE);
+ goto fail_reject;
+ }
+ ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,
+ (JSValueConst *)resolving_funcs);
+ if (check_exception_free(ctx, ret))
+ goto fail_reject1;
+ }
+ }
+ done:
+ JS_FreeValue(ctx, promise_resolve);
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return result_promise;
+ fail:
+ //JS_FreeValue(ctx, next_method); // why not???
+ JS_FreeValue(ctx, result_promise);
+ result_promise = JS_EXCEPTION;
+ goto done;
+}
+
+static warn_unused int perform_promise_then(JSContext *ctx,
+ JSValueConst promise,
+ JSValueConst *resolve_reject,
+ JSValueConst *cap_resolving_funcs)
+{
+ JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
+ JSPromiseReactionData *rd_array[2], *rd;
+ int i, j;
+
+ rd_array[0] = NULL;
+ rd_array[1] = NULL;
+ for(i = 0; i < 2; i++) {
+ JSValueConst handler;
+ rd = js_mallocz(ctx, sizeof(*rd));
+ if (!rd) {
+ if (i == 1)
+ promise_reaction_data_free(ctx->rt, rd_array[0]);
+ return -1;
+ }
+ for(j = 0; j < 2; j++)
+ rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);
+ handler = resolve_reject[i];
+ if (!JS_IsFunction(ctx, handler))
+ handler = JS_UNDEFINED;
+ rd->handler = JS_DupValue(ctx, handler);
+ rd_array[i] = rd;
+ }
+
+ if (s->promise_state == JS_PROMISE_PENDING) {
+ for(i = 0; i < 2; i++)
+ list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);
+ } else {
+ JSValueConst args[5];
+ if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
+ JSRuntime *rt = ctx->rt;
+ if (rt->host_promise_rejection_tracker) {
+ rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
+ TRUE, rt->host_promise_rejection_tracker_opaque);
+ }
+ }
+ i = s->promise_state - JS_PROMISE_FULFILLED;
+ rd = rd_array[i];
+ args[0] = rd->resolving_funcs[0];
+ args[1] = rd->resolving_funcs[1];
+ args[2] = rd->handler;
+ args[3] = JS_NewBool(ctx, i);
+ args[4] = s->promise_result;
+ JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
+ for(i = 0; i < 2; i++)
+ promise_reaction_data_free(ctx->rt, rd_array[i]);
+ }
+ s->is_handled = TRUE;
+ return 0;
+}
+
+static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue ctor, result_promise, resolving_funcs[2];
+ JSPromiseData *s;
+ int i, ret;
+
+ s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);
+ if (!s)
+ return JS_EXCEPTION;
+
+ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ return ctor;
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
+ JS_FreeValue(ctx, ctor);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ ret = perform_promise_then(ctx, this_val, argv,
+ (JSValueConst *)resolving_funcs);
+ for(i = 0; i < 2; i++)
+ JS_FreeValue(ctx, resolving_funcs[i]);
+ if (ret) {
+ JS_FreeValue(ctx, result_promise);
+ return JS_EXCEPTION;
+ }
+ return result_promise;
+}
+
+static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst args[2];
+ args[0] = JS_UNDEFINED;
+ args[1] = argv[0];
+ return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);
+}
+
+static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ return JS_DupValue(ctx, func_data[0]);
+}
+
+static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));
+}
+
+static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ JSValueConst ctor = func_data[0];
+ JSValueConst onFinally = func_data[1];
+ JSValue res, promise, ret, then_func;
+
+ res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
+ if (JS_IsException(res))
+ return res;
+ promise = js_promise_resolve(ctx, ctor, 1, &res, 0);
+ JS_FreeValue(ctx, res);
+ if (JS_IsException(promise))
+ return promise;
+ if (magic == 0) {
+ then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,
+ 0, 1, argv);
+ } else {
+ then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,
+ 0, 1, argv);
+ }
+ if (JS_IsException(then_func)) {
+ JS_FreeValue(ctx, promise);
+ return then_func;
+ }
+ ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, &then_func);
+ JS_FreeValue(ctx, then_func);
+ return ret;
+}
+
+static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst onFinally = argv[0];
+ JSValue ctor, ret;
+ JSValue then_funcs[2];
+ JSValueConst func_data[2];
+ int i;
+
+ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ return ctor;
+ if (!JS_IsFunction(ctx, onFinally)) {
+ then_funcs[0] = JS_DupValue(ctx, onFinally);
+ then_funcs[1] = JS_DupValue(ctx, onFinally);
+ } else {
+ func_data[0] = ctor;
+ func_data[1] = onFinally;
+ for(i = 0; i < 2; i++) {
+ then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);
+ if (JS_IsException(then_funcs[i])) {
+ if (i == 1)
+ JS_FreeValue(ctx, then_funcs[0]);
+ JS_FreeValue(ctx, ctor);
+ return JS_EXCEPTION;
+ }
+ }
+ }
+ JS_FreeValue(ctx, ctor);
+ ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);
+ JS_FreeValue(ctx, then_funcs[0]);
+ JS_FreeValue(ctx, then_funcs[1]);
+ return ret;
+}
+
+static const JSCFunctionListEntry js_promise_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),
+ JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),
+ JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),
+ JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
+ JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
+ JS_CFUNC_DEF("race", 1, js_promise_race ),
+ //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
+};
+
+static const JSCFunctionListEntry js_promise_proto_funcs[] = {
+ JS_CFUNC_DEF("then", 2, js_promise_then ),
+ JS_CFUNC_DEF("catch", 1, js_promise_catch ),
+ JS_CFUNC_DEF("finally", 1, js_promise_finally ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),
+};
+
+/* AsyncFunction */
+static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
+};
+
+static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),
+ JS_ToBool(ctx, func_data[0]));
+}
+
+static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
+ BOOL done)
+{
+ JSValueConst func_data[1];
+
+ func_data[0] = JS_NewBool(ctx, done);
+ return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
+ 1, 0, 1, func_data);
+}
+
+/* AsyncIteratorPrototype */
+
+static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
+ JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
+};
+
+/* AsyncFromSyncIteratorPrototype */
+
+typedef struct JSAsyncFromSyncIteratorData {
+ JSValue sync_iter;
+ JSValue next_method;
+} JSAsyncFromSyncIteratorData;
+
+static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSAsyncFromSyncIteratorData *s =
+ JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
+ if (s) {
+ JS_FreeValueRT(rt, s->sync_iter);
+ JS_FreeValueRT(rt, s->next_method);
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSAsyncFromSyncIteratorData *s =
+ JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
+ if (s) {
+ JS_MarkValue(rt, s->sync_iter, mark_func);
+ JS_MarkValue(rt, s->next_method, mark_func);
+ }
+}
+
+static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
+ JSValueConst sync_iter)
+{
+ JSValue async_iter, next_method;
+ JSAsyncFromSyncIteratorData *s;
+
+ next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ return JS_EXCEPTION;
+ async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
+ if (JS_IsException(async_iter)) {
+ JS_FreeValue(ctx, next_method);
+ return async_iter;
+ }
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s) {
+ JS_FreeValue(ctx, async_iter);
+ JS_FreeValue(ctx, next_method);
+ return JS_EXCEPTION;
+ }
+ s->sync_iter = JS_DupValue(ctx, sync_iter);
+ s->next_method = next_method;
+ JS_SetOpaque(async_iter, s);
+ return async_iter;
+}
+
+static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic)
+{
+ JSValue promise, resolving_funcs[2], value, err, method;
+ JSAsyncFromSyncIteratorData *s;
+ int done;
+ int is_reject;
+
+ promise = JS_NewPromiseCapability(ctx, resolving_funcs);
+ if (JS_IsException(promise))
+ return JS_EXCEPTION;
+ s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
+ if (!s) {
+ JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");
+ goto reject;
+ }
+
+ if (magic == GEN_MAGIC_NEXT) {
+ method = JS_DupValue(ctx, s->next_method);
+ } else {
+ method = JS_GetProperty(ctx, s->sync_iter,
+ magic == GEN_MAGIC_RETURN ? JS_ATOM_return :
+ JS_ATOM_throw);
+ if (JS_IsException(method))
+ goto reject;
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
+ if (magic == GEN_MAGIC_RETURN) {
+ err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
+ is_reject = 0;
+ } else {
+ err = JS_DupValue(ctx, argv[0]);
+ is_reject = 1;
+ }
+ goto done_resolve;
+ }
+ }
+ value = JS_IteratorNext2(ctx, s->sync_iter, method,
+ argc >= 1 ? 1 : 0, argv, &done);
+ JS_FreeValue(ctx, method);
+ if (JS_IsException(value))
+ goto reject;
+ if (done == 2) {
+ JSValue obj = value;
+ value = JS_IteratorGetCompleteValue(ctx, obj, &done);
+ JS_FreeValue(ctx, obj);
+ if (JS_IsException(value))
+ goto reject;
+ }
+
+ if (JS_IsException(value)) {
+ JSValue res2;
+ reject:
+ err = JS_GetException(ctx);
+ is_reject = 1;
+ done_resolve:
+ res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
+ 1, &err);
+ JS_FreeValue(ctx, err);
+ JS_FreeValue(ctx, res2);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return promise;
+ }
+ {
+ JSValue value_wrapper_promise, resolve_reject[2];
+ int res;
+
+ value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, &value, 0);
+ if (JS_IsException(value_wrapper_promise)) {
+ JS_FreeValue(ctx, value);
+ goto reject;
+ }
+
+ resolve_reject[0] =
+ js_async_from_sync_iterator_unwrap_func_create(ctx, done);
+ if (JS_IsException(resolve_reject[0])) {
+ JS_FreeValue(ctx, value_wrapper_promise);
+ goto fail;
+ }
+ JS_FreeValue(ctx, value);
+ resolve_reject[1] = JS_UNDEFINED;
+
+ res = perform_promise_then(ctx, value_wrapper_promise,
+ (JSValueConst *)resolve_reject,
+ (JSValueConst *)resolving_funcs);
+ JS_FreeValue(ctx, resolve_reject[0]);
+ JS_FreeValue(ctx, value_wrapper_promise);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ if (res) {
+ JS_FreeValue(ctx, promise);
+ return JS_EXCEPTION;
+ }
+ }
+ return promise;
+ fail:
+ JS_FreeValue(ctx, value);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ JS_FreeValue(ctx, promise);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),
+ JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),
+ JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),
+};
+
+/* AsyncGeneratorFunction */
+
+static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),
+};
+
+/* AsyncGenerator prototype */
+
+static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),
+ JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),
+ JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),
+};
+
+static JSClassShortDef const js_async_class_def[] = {
+ { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */
+ { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */
+ { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */
+ { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */
+ { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */
+ { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */
+ { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
+ { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
+ { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */
+};
+
+void JS_AddIntrinsicPromise(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValue obj1;
+
+ if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {
+ init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,
+ countof(js_async_class_def));
+ rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;
+ rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;
+ rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;
+ rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;
+ rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;
+ rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;
+ }
+
+ /* Promise */
+ ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],
+ js_promise_proto_funcs,
+ countof(js_promise_proto_funcs));
+ obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,
+ JS_CFUNC_constructor, 0);
+ ctx->promise_ctor = JS_DupValue(ctx, obj1);
+ JS_SetPropertyFunctionList(ctx, obj1,
+ js_promise_funcs,
+ countof(js_promise_funcs));
+ JS_NewGlobalCConstructor2(ctx, obj1, "Promise",
+ ctx->class_proto[JS_CLASS_PROMISE]);
+
+ /* AsyncFunction */
+ ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
+ obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
+ "AsyncFunction", 1,
+ JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,
+ ctx->function_ctor);
+ JS_SetPropertyFunctionList(ctx,
+ ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
+ js_async_function_proto_funcs,
+ countof(js_async_function_proto_funcs));
+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
+ 0, JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+
+ /* AsyncIteratorPrototype */
+ ctx->async_iterator_proto = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,
+ js_async_iterator_proto_funcs,
+ countof(js_async_iterator_proto_funcs));
+
+ /* AsyncFromSyncIteratorPrototype */
+ ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =
+ JS_NewObjectProto(ctx, ctx->async_iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],
+ js_async_from_sync_iterator_proto_funcs,
+ countof(js_async_from_sync_iterator_proto_funcs));
+
+ /* AsyncGeneratorPrototype */
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =
+ JS_NewObjectProto(ctx, ctx->async_iterator_proto);
+ JS_SetPropertyFunctionList(ctx,
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
+ js_async_generator_proto_funcs,
+ countof(js_async_generator_proto_funcs));
+
+ /* AsyncGeneratorFunction */
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =
+ JS_NewObjectProto(ctx, ctx->function_proto);
+ obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
+ "AsyncGeneratorFunction", 1,
+ JS_CFUNC_constructor_or_func_magic,
+ JS_FUNC_ASYNC_GENERATOR,
+ ctx->function_ctor);
+ JS_SetPropertyFunctionList(ctx,
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
+ js_async_generator_function_proto_funcs,
+ countof(js_async_generator_function_proto_funcs));
+ JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
+ JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
+ 0, JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+}
+
+/* URI handling */
+
+static int string_get_hex(JSString *p, int k, int n) {
+ int c = 0, h;
+ while (n-- > 0) {
+ if ((h = from_hex(string_get(p, k++))) < 0)
+ return -1;
+ c = (c << 4) | h;
+ }
+ return c;
+}
+
+static int isURIReserved(int c) {
+ return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
+}
+
+static int FORMAT_ATTR(2, 3) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);
+ va_end(ap);
+ return -1;
+}
+
+static int hex_decode(JSContext *ctx, JSString *p, int k) {
+ int c;
+
+ if (k >= p->len || string_get(p, k) != '%')
+ return js_throw_URIError(ctx, "expecting %%");
+ if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
+ return js_throw_URIError(ctx, "expecting hex digit");
+
+ return c;
+}
+
+static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int isComponent)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int k, c, c1, n, c_min;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+
+ string_buffer_init(ctx, b, 0);
+
+ p = JS_VALUE_GET_STRING(str);
+ for (k = 0; k < p->len;) {
+ c = string_get(p, k);
+ if (c == '%') {
+ c = hex_decode(ctx, p, k);
+ if (c < 0)
+ goto fail;
+ k += 3;
+ if (c < 0x80) {
+ if (!isComponent && isURIReserved(c)) {
+ c = '%';
+ k -= 2;
+ }
+ } else {
+ /* Decode URI-encoded UTF-8 sequence */
+ if (c >= 0xc0 && c <= 0xdf) {
+ n = 1;
+ c_min = 0x80;
+ c &= 0x1f;
+ } else if (c >= 0xe0 && c <= 0xef) {
+ n = 2;
+ c_min = 0x800;
+ c &= 0xf;
+ } else if (c >= 0xf0 && c <= 0xf7) {
+ n = 3;
+ c_min = 0x10000;
+ c &= 0x7;
+ } else {
+ n = 0;
+ c_min = 1;
+ c = 0;
+ }
+ while (n-- > 0) {
+ c1 = hex_decode(ctx, p, k);
+ if (c1 < 0)
+ goto fail;
+ k += 3;
+ if ((c1 & 0xc0) != 0x80) {
+ c = 0;
+ break;
+ }
+ c = (c << 6) | (c1 & 0x3f);
+ }
+ if (c < c_min || c > 0x10FFFF ||
+ (c >= 0xd800 && c < 0xe000)) {
+ js_throw_URIError(ctx, "malformed UTF-8");
+ goto fail;
+ }
+ }
+ } else {
+ k++;
+ }
+ string_buffer_putc(b, c);
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+fail:
+ JS_FreeValue(ctx, str);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static int isUnescaped(int c) {
+ static char const unescaped_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "@*_+-./";
+ return c < 0x100 &&
+ memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
+}
+
+static int isURIUnescaped(int c, int isComponent) {
+ return c < 0x100 &&
+ ((c >= 0x61 && c <= 0x7a) ||
+ (c >= 0x41 && c <= 0x5a) ||
+ (c >= 0x30 && c <= 0x39) ||
+ memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
+ (!isComponent && isURIReserved(c)));
+}
+
+static int encodeURI_hex(StringBuffer *b, int c) {
+ uint8_t buf[6];
+ int n = 0;
+ const char *hex = "0123456789ABCDEF";
+
+ buf[n++] = '%';
+ if (c >= 256) {
+ buf[n++] = 'u';
+ buf[n++] = hex[(c >> 12) & 15];
+ buf[n++] = hex[(c >> 8) & 15];
+ }
+ buf[n++] = hex[(c >> 4) & 15];
+ buf[n++] = hex[(c >> 0) & 15];
+ return string_buffer_write8(b, buf, n);
+}
+
+static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int isComponent)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int k, c, c1;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+
+ p = JS_VALUE_GET_STRING(str);
+ string_buffer_init(ctx, b, p->len);
+ for (k = 0; k < p->len;) {
+ c = string_get(p, k);
+ k++;
+ if (isURIUnescaped(c, isComponent)) {
+ string_buffer_putc16(b, c);
+ } else {
+ if (c >= 0xdc00 && c <= 0xdfff) {
+ js_throw_URIError(ctx, "invalid character");
+ goto fail;
+ } else if (c >= 0xd800 && c <= 0xdbff) {
+ if (k >= p->len) {
+ js_throw_URIError(ctx, "expecting surrogate pair");
+ goto fail;
+ }
+ c1 = string_get(p, k);
+ k++;
+ if (c1 < 0xdc00 || c1 > 0xdfff) {
+ js_throw_URIError(ctx, "expecting surrogate pair");
+ goto fail;
+ }
+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
+ }
+ if (c < 0x80) {
+ encodeURI_hex(b, c);
+ } else {
+ /* XXX: use C UTF-8 conversion ? */
+ if (c < 0x800) {
+ encodeURI_hex(b, (c >> 6) | 0xc0);
+ } else {
+ if (c < 0x10000) {
+ encodeURI_hex(b, (c >> 12) | 0xe0);
+ } else {
+ encodeURI_hex(b, (c >> 18) | 0xf0);
+ encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
+ }
+ encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
+ }
+ encodeURI_hex(b, (c & 0x3f) | 0x80);
+ }
+ }
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+fail:
+ JS_FreeValue(ctx, str);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int i, len, c;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+
+ p = JS_VALUE_GET_STRING(str);
+ string_buffer_init(ctx, b, p->len);
+ for (i = 0, len = p->len; i < len; i++) {
+ c = string_get(p, i);
+ if (isUnescaped(c)) {
+ string_buffer_putc16(b, c);
+ } else {
+ encodeURI_hex(b, c);
+ }
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+}
+
+static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int i, len, c, n;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+
+ string_buffer_init(ctx, b, 0);
+ p = JS_VALUE_GET_STRING(str);
+ for (i = 0, len = p->len; i < len; i++) {
+ c = string_get(p, i);
+ if (c == '%') {
+ if (i + 6 <= len
+ && string_get(p, i + 1) == 'u'
+ && (n = string_get_hex(p, i + 2, 4)) >= 0) {
+ c = n;
+ i += 6 - 1;
+ } else
+ if (i + 3 <= len
+ && (n = string_get_hex(p, i + 1, 2)) >= 0) {
+ c = n;
+ i += 3 - 1;
+ }
+ }
+ string_buffer_putc16(b, c);
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+}
+
+/* global object */
+
+static const JSCFunctionListEntry js_global_funcs[] = {
+ JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
+ JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
+ JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
+ JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
+
+ JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
+ JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
+ JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
+ JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
+ JS_CFUNC_DEF("escape", 1, js_global_escape ),
+ JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
+ JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
+ JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
+ JS_PROP_UNDEFINED_DEF("undefined", 0 ),
+
+ /* for the 'Date' implementation */
+ JS_CFUNC_DEF("__date_clock", 0, js___date_clock ),
+ //JS_CFUNC_DEF("__date_now", 0, js___date_now ),
+ //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ),
+ //JS_CFUNC_DEF("__date_create", 3, js___date_create ),
+};
+
+/* Date */
+
+static int64_t math_mod(int64_t a, int64_t b) {
+ /* return positive modulo */
+ int64_t m = a % b;
+ return m + (m < 0) * b;
+}
+
+static int64_t floor_div(int64_t a, int64_t b) {
+ /* integer division rounding toward -Infinity */
+ int64_t m = a % b;
+ return (a - (m + (m < 0) * b)) / b;
+}
+
+static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+static warn_unused int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
+ return JS_ToFloat64(ctx, valp, p->u.object_data);
+ }
+ JS_ThrowTypeError(ctx, "not a Date object");
+ return -1;
+}
+
+static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_DATE) {
+ JS_FreeValue(ctx, p->u.object_data);
+ p->u.object_data = JS_NewFloat64(ctx, v);
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a Date object");
+}
+
+static int64_t days_from_year(int64_t y) {
+ return 365 * (y - 1970) + floor_div(y - 1969, 4) -
+ floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
+}
+
+static int64_t days_in_year(int64_t y) {
+ return 365 + !(y % 4) - !(y % 100) + !(y % 400);
+}
+
+/* return the year, update days */
+static int64_t year_from_days(int64_t *days) {
+ int64_t y, d1, nd, d = *days;
+ y = floor_div(d * 10000, 3652425) + 1970;
+ /* the initial approximation is very good, so only a few
+ iterations are necessary */
+ for(;;) {
+ d1 = d - days_from_year(y);
+ if (d1 < 0) {
+ y--;
+ d1 += days_in_year(y);
+ } else {
+ nd = days_in_year(y);
+ if (d1 < nd)
+ break;
+ d1 -= nd;
+ y++;
+ }
+ }
+ *days = d1;
+ return y;
+}
+
+static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+static char const day_names[] = "SunMonTueWedThuFriSat";
+
+static warn_unused int get_date_fields(JSContext *ctx, JSValueConst obj,
+ double fields[9], int is_local, int force)
+{
+ double dval;
+ int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
+
+ if (JS_ThisTimeValue(ctx, &dval, obj))
+ return -1;
+
+ if (isnan(dval)) {
+ if (!force)
+ return FALSE; /* NaN */
+ d = 0; /* initialize all fields to 0 */
+ } else {
+ d = dval;
+ if (is_local) {
+ tz = -getTimezoneOffset(d);
+ d += tz * 60000;
+ }
+ }
+
+ /* result is >= 0, we can use % */
+ h = math_mod(d, 86400000);
+ days = (d - h) / 86400000;
+ ms = h % 1000;
+ h = (h - ms) / 1000;
+ s = h % 60;
+ h = (h - s) / 60;
+ m = h % 60;
+ h = (h - m) / 60;
+ wd = math_mod(days + 4, 7); /* week day */
+ y = year_from_days(&days);
+
+ for(i = 0; i < 11; i++) {
+ md = month_days[i];
+ if (i == 1)
+ md += days_in_year(y) - 365;
+ if (days < md)
+ break;
+ days -= md;
+ }
+ fields[0] = y;
+ fields[1] = i;
+ fields[2] = days + 1;
+ fields[3] = h;
+ fields[4] = m;
+ fields[5] = s;
+ fields[6] = ms;
+ fields[7] = wd;
+ fields[8] = tz;
+ return TRUE;
+}
+
+static double time_clip(double t) {
+ if (t >= -8.64e15 && t <= 8.64e15)
+ return trunc(t) + 0.0; /* convert -0 to +0 */
+ else
+ return NAN;
+}
+
+/* The spec mandates the use of 'double' and it fixes the order
+ of the operations */
+static double set_date_fields(const double fields[], int is_local) {
+ int64_t y;
+ double days, d, h, m1;
+ int i, m, md;
+
+ m1 = fields[1];
+ m = fmod(m1, 12);
+ if (m < 0)
+ m += 12;
+ y = (int64_t)(fields[0] + floor(m1 / 12));
+ days = days_from_year(y);
+
+ for(i = 0; i < m; i++) {
+ md = month_days[i];
+ if (i == 1)
+ md += days_in_year(y) - 365;
+ days += md;
+ }
+ days += fields[2] - 1;
+ h = fields[3] * 3600000 + fields[4] * 60000 +
+ fields[5] * 1000 + fields[6];
+ d = days * 86400000 + h;
+ if (is_local)
+ d += getTimezoneOffset(d) * 60000;
+ return time_clip(d);
+}
+
+static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ // get_date_field(obj, n, is_local)
+ double fields[9];
+ int res, n, is_local;
+
+ is_local = magic & 0x0F;
+ n = (magic >> 4) & 0x0F;
+ res = get_date_fields(ctx, this_val, fields, is_local, 0);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res)
+ return JS_NAN;
+
+ if (magic & 0x100) { // getYear
+ fields[0] -= 1900;
+ }
+ return JS_NewFloat64(ctx, fields[n]);
+}
+
+static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ // _field(obj, first_field, end_field, args, is_local)
+ double fields[9];
+ int res, first_field, end_field, is_local, i, n;
+ double d, a;
+
+ d = NAN;
+ first_field = (magic >> 8) & 0x0F;
+ end_field = (magic >> 4) & 0x0F;
+ is_local = magic & 0x0F;
+
+ res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (res && argc > 0) {
+ n = end_field - first_field;
+ if (argc < n)
+ n = argc;
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ goto done;
+ fields[first_field + i] = trunc(a);
+ }
+ d = set_date_fields(fields, is_local);
+ }
+done:
+ return JS_SetThisTimeValue(ctx, this_val, d);
+}
+
+/* fmt:
+ 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
+ 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
+ 2: toISOString: "2018-01-02T23:02:56.927Z"
+ 3: toLocaleString: "1/2/2018, 11:40:40 PM"
+ part: 1=date, 2=time 3=all
+ XXX: should use a variant of strftime().
+ */
+static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ // _string(obj, fmt, part)
+ char buf[64];
+ double fields[9];
+ int res, fmt, part, pos;
+ int y, mon, d, h, m, s, ms, wd, tz;
+
+ fmt = (magic >> 4) & 0x0F;
+ part = magic & 0x0F;
+
+ res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res) {
+ if (fmt == 2)
+ return JS_ThrowRangeError(ctx, "Date value is NaN");
+ else
+ return JS_NewString(ctx, "Invalid Date");
+ }
+
+ y = fields[0];
+ mon = fields[1];
+ d = fields[2];
+ h = fields[3];
+ m = fields[4];
+ s = fields[5];
+ ms = fields[6];
+ wd = fields[7];
+ tz = fields[8];
+
+ pos = 0;
+
+ if (part & 1) { /* date part */
+ switch(fmt) {
+ case 0:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%.3s, %02d %.3s %0*d ",
+ day_names + wd * 3, d,
+ month_names + mon * 3, 4 + (y < 0), y);
+ break;
+ case 1:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%.3s %.3s %02d %0*d",
+ day_names + wd * 3,
+ month_names + mon * 3, d, 4 + (y < 0), y);
+ if (part == 3) {
+ buf[pos++] = ' ';
+ }
+ break;
+ case 2:
+ if (y >= 0 && y <= 9999) {
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%04d", y);
+ } else {
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%+07d", y);
+ }
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "-%02d-%02dT", mon + 1, d);
+ break;
+ case 3:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
+ if (part == 3) {
+ buf[pos++] = ',';
+ buf[pos++] = ' ';
+ }
+ break;
+ }
+ }
+ if (part & 2) { /* time part */
+ switch(fmt) {
+ case 0:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d GMT", h, m, s);
+ break;
+ case 1:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d GMT", h, m, s);
+ if (tz < 0) {
+ buf[pos++] = '-';
+ tz = -tz;
+ } else {
+ buf[pos++] = '+';
+ }
+ /* tz is >= 0, can use % */
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d%02d", tz / 60, tz % 60);
+ /* XXX: tack the time zone code? */
+ break;
+ case 2:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d.%03dZ", h, m, s, ms);
+ break;
+ case 3:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s,
+ (h < 12) ? 'A' : 'P');
+ break;
+ }
+ }
+ return JS_NewStringLen(ctx, buf, pos);
+}
+
+/* OS dependent: return the UTC time in ms since 1970. */
+static int64_t date_now(void) {
+#ifdef _MSC_VER
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ int64_t d;
+ SystemTimeToFileTime(&st, (FILETIME *) &d);
+ return d /= 10000;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
+#endif
+}
+
+static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ // Date(y, mon, d, h, m, s, ms)
+ JSValue rv;
+ int i, n;
+ double a, val;
+
+ if (JS_IsUndefined(new_target)) {
+ /* invoked as function */
+ argc = 0;
+ }
+ n = argc;
+ if (n == 0) {
+ val = date_now();
+ } else if (n == 1) {
+ JSValue v, dv;
+ if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
+ if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
+ if (JS_ToFloat64(ctx, &val, p->u.object_data))
+ return JS_EXCEPTION;
+ val = time_clip(val);
+ goto has_val;
+ }
+ }
+ v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
+ if (JS_IsString(v)) {
+ dv = js_Date_parse(ctx, JS_UNDEFINED, 1, &v);
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(dv))
+ return JS_EXCEPTION;
+ if (JS_ToFloat64Free(ctx, &val, dv))
+ return JS_EXCEPTION;
+ } else {
+ if (JS_ToFloat64Free(ctx, &val, v))
+ return JS_EXCEPTION;
+ }
+ val = time_clip(val);
+ } else {
+ double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
+ if (n > 7)
+ n = 7;
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ break;
+ fields[i] = trunc(a);
+ if (i == 0 && fields[0] >= 0 && fields[0] < 100)
+ fields[0] += 1900;
+ }
+ val = (i == n) ? set_date_fields(fields, 1) : NAN;
+ }
+has_val:
+#if 0
+ JSValueConst args[3];
+ args[0] = new_target;
+ args[1] = ctx->class_proto[JS_CLASS_DATE];
+ args[2] = JS_NewFloat64(ctx, val);
+ rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
+#else
+ rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
+ if (!JS_IsException(rv))
+ JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
+#endif
+ if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
+ /* invoked as a function, return (new Date()).toString(); */
+ JSValue s;
+ s = get_date_string(ctx, rv, 0, NULL, 0x13);
+ JS_FreeValue(ctx, rv);
+ rv = s;
+ }
+ return rv;
+}
+
+static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // UTC(y, mon, d, h, m, s, ms)
+ double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
+ int i, n;
+ double a;
+
+ n = argc;
+ if (n == 0)
+ return JS_NAN;
+ if (n > 7)
+ n = 7;
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ return JS_NAN;
+ fields[i] = trunc(a);
+ if (i == 0 && fields[0] >= 0 && fields[0] < 100)
+ fields[0] += 1900;
+ }
+ return JS_NewFloat64(ctx, set_date_fields(fields, 0));
+}
+
+static void string_skip_spaces(JSString *sp, int *pp) {
+ while (*pp < sp->len && string_get(sp, *pp) == ' ')
+ *pp += 1;
+}
+
+static void string_skip_non_spaces(JSString *sp, int *pp) {
+ while (*pp < sp->len && string_get(sp, *pp) != ' ')
+ *pp += 1;
+}
+
+/* parse a numeric field with an optional sign if accept_sign is TRUE */
+static int string_get_digits(JSString *sp, int *pp, int64_t *pval) {
+ int64_t v = 0;
+ int c, p = *pp, p_start;
+
+ if (p >= sp->len)
+ return -1;
+ p_start = p;
+ while (p < sp->len) {
+ c = string_get(sp, p);
+ if (!(c >= '0' && c <= '9')) {
+ if (p == p_start)
+ return -1;
+ else
+ break;
+ }
+ v = v * 10 + c - '0';
+ p++;
+ }
+ *pval = v;
+ *pp = p;
+ return 0;
+}
+
+static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) {
+ int res, sgn, p = *pp;
+
+ if (p >= sp->len)
+ return -1;
+
+ sgn = string_get(sp, p);
+ if (sgn == '-' || sgn == '+')
+ p++;
+
+ res = string_get_digits(sp, &p, pval);
+ if (res == 0 && sgn == '-')
+ *pval = -*pval;
+ *pp = p;
+ return res;
+}
+
+/* parse a fixed width numeric field */
+static int string_get_fixed_width_digits(JSString *sp, int *pp, int n, int64_t *pval) {
+ int64_t v = 0;
+ int i, c, p = *pp;
+
+ for(i = 0; i < n; i++) {
+ if (p >= sp->len)
+ return -1;
+ c = string_get(sp, p);
+ if (!(c >= '0' && c <= '9'))
+ return -1;
+ v = v * 10 + c - '0';
+ p++;
+ }
+ *pval = v;
+ *pp = p;
+ return 0;
+}
+
+static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) {
+ /* parse milliseconds as a fractional part, round to nearest */
+ /* XXX: the spec does not indicate which rounding should be used */
+ int mul = 1000, ms = 0, p = *pp, c, p_start;
+ if (p >= sp->len)
+ return -1;
+ p_start = p;
+ while (p < sp->len) {
+ c = string_get(sp, p);
+ if (!(c >= '0' && c <= '9')) {
+ if (p == p_start)
+ return -1;
+ else
+ break;
+ }
+ if (mul == 1 && c >= '5')
+ ms += 1;
+ ms += (c - '0') * (mul /= 10);
+ p++;
+ }
+ *pval = ms;
+ *pp = p;
+ return 0;
+}
+
+
+static int find_abbrev(JSString *sp, int p, const char *list, int count) {
+ int n, i;
+
+ if (p + 3 <= sp->len) {
+ for (n = 0; n < count; n++) {
+ for (i = 0; i < 3; i++) {
+ if (string_get(sp, p + i) != month_names[n * 3 + i])
+ goto next;
+ }
+ return n;
+ next:;
+ }
+ }
+ return -1;
+}
+
+static int string_get_month(JSString *sp, int *pp, int64_t *pval) {
+ int n;
+
+ string_skip_spaces(sp, pp);
+ n = find_abbrev(sp, *pp, month_names, 12);
+ if (n < 0)
+ return -1;
+
+ *pval = n;
+ *pp += 3;
+ return 0;
+}
+
+static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // parse(s)
+ JSValue s, rv;
+ int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 };
+ double fields1[7];
+ int64_t tz, hh, mm;
+ double d;
+ int p, i, c, sgn, l;
+ JSString *sp;
+ BOOL is_local;
+
+ rv = JS_NAN;
+
+ s = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(s))
+ return JS_EXCEPTION;
+
+ sp = JS_VALUE_GET_STRING(s);
+ p = 0;
+ if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {
+ /* ISO format */
+ /* year field can be negative */
+ if (string_get_signed_digits(sp, &p, &fields[0]))
+ goto done;
+
+ for (i = 1; i < 7; i++) {
+ if (p >= sp->len)
+ break;
+ switch(i) {
+ case 1:
+ case 2:
+ c = '-';
+ break;
+ case 3:
+ c = 'T';
+ break;
+ case 4:
+ case 5:
+ c = ':';
+ break;
+ case 6:
+ c = '.';
+ break;
+ }
+ if (string_get(sp, p) != c)
+ break;
+ p++;
+ if (i == 6) {
+ if (string_get_milliseconds(sp, &p, &fields[i]))
+ goto done;
+ } else {
+ if (string_get_digits(sp, &p, &fields[i]))
+ goto done;
+ }
+ }
+ /* no time: UTC by default */
+ is_local = (i > 3);
+ fields[1] -= 1;
+
+ /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
+ tz = 0;
+ if (p < sp->len) {
+ sgn = string_get(sp, p);
+ if (sgn == '+' || sgn == '-') {
+ p++;
+ l = sp->len - p;
+ if (l != 4 && l != 5)
+ goto done;
+ if (string_get_fixed_width_digits(sp, &p, 2, &hh))
+ goto done;
+ if (l == 5) {
+ if (string_get(sp, p) != ':')
+ goto done;
+ p++;
+ }
+ if (string_get_fixed_width_digits(sp, &p, 2, &mm))
+ goto done;
+ tz = hh * 60 + mm;
+ if (sgn == '-')
+ tz = -tz;
+ is_local = FALSE;
+ } else if (sgn == 'Z') {
+ p++;
+ is_local = FALSE;
+ } else {
+ goto done;
+ }
+ /* error if extraneous characters */
+ if (p != sp->len)
+ goto done;
+ }
+ } else {
+ /* toString or toUTCString format */
+ /* skip the day of the week */
+ string_skip_non_spaces(sp, &p);
+ string_skip_spaces(sp, &p);
+ if (p >= sp->len)
+ goto done;
+ c = string_get(sp, p);
+ if (c >= '0' && c <= '9') {
+ /* day of month first */
+ if (string_get_digits(sp, &p, &fields[2]))
+ goto done;
+ if (string_get_month(sp, &p, &fields[1]))
+ goto done;
+ } else {
+ /* month first */
+ if (string_get_month(sp, &p, &fields[1]))
+ goto done;
+ string_skip_spaces(sp, &p);
+ if (string_get_digits(sp, &p, &fields[2]))
+ goto done;
+ }
+ /* year */
+ string_skip_spaces(sp, &p);
+ if (string_get_signed_digits(sp, &p, &fields[0]))
+ goto done;
+
+ /* hour, min, seconds */
+ string_skip_spaces(sp, &p);
+ for(i = 0; i < 3; i++) {
+ if (i == 1 || i == 2) {
+ if (p >= sp->len)
+ goto done;
+ if (string_get(sp, p) != ':')
+ goto done;
+ p++;
+ }
+ if (string_get_digits(sp, &p, &fields[3 + i]))
+ goto done;
+ }
+ // XXX: parse optional milliseconds?
+
+ /* parse the time zone offset if present: [+-]HHmm */
+ is_local = FALSE;
+ tz = 0;
+ for (tz = 0; p < sp->len; p++) {
+ sgn = string_get(sp, p);
+ if (sgn == '+' || sgn == '-') {
+ p++;
+ if (string_get_fixed_width_digits(sp, &p, 2, &hh))
+ goto done;
+ if (string_get_fixed_width_digits(sp, &p, 2, &mm))
+ goto done;
+ tz = hh * 60 + mm;
+ if (sgn == '-')
+ tz = -tz;
+ break;
+ }
+ }
+ }
+ for(i = 0; i < 7; i++)
+ fields1[i] = fields[i];
+ d = set_date_fields(fields1, is_local) - tz * 60000;
+ rv = JS_NewFloat64(ctx, d);
+
+done:
+ JS_FreeValue(ctx, s);
+ return rv;
+}
+
+static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // now()
+ return JS_NewInt64(ctx, date_now());
+}
+
+static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // Symbol_toPrimitive(hint)
+ JSValueConst obj = this_val;
+ JSAtom hint = JS_ATOM_NULL;
+ int hint_num;
+
+ if (!JS_IsObject(obj))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ if (JS_IsString(argv[0])) {
+ hint = JS_ValueToAtom(ctx, argv[0]);
+ if (hint == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+ JS_FreeAtom(ctx, hint);
+ }
+ switch (hint) {
+ case JS_ATOM_number:
+#ifdef CONFIG_BIGNUM
+ case JS_ATOM_integer:
+#endif
+ hint_num = HINT_NUMBER;
+ break;
+ case JS_ATOM_string:
+ case JS_ATOM_default:
+ hint_num = HINT_STRING;
+ break;
+ default:
+ return JS_ThrowTypeError(ctx, "invalid hint");
+ }
+ return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
+}
+
+static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // getTimezoneOffset()
+ double v;
+
+ if (JS_ThisTimeValue(ctx, &v, this_val))
+ return JS_EXCEPTION;
+ if (isnan(v))
+ return JS_NAN;
+ else
+ return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
+}
+
+static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // getTime()
+ double v;
+
+ if (JS_ThisTimeValue(ctx, &v, this_val))
+ return JS_EXCEPTION;
+ return JS_NewFloat64(ctx, v);
+}
+
+static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // setTime(v)
+ double v;
+
+ if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
+ return JS_EXCEPTION;
+ return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
+}
+
+static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // setYear(y)
+ double y;
+ JSValueConst args[1];
+
+ if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
+ return JS_EXCEPTION;
+ y = +y;
+ if (isfinite(y)) {
+ y = trunc(y);
+ if (y >= 0 && y < 100)
+ y += 1900;
+ }
+ args[0] = JS_NewFloat64(ctx, y);
+ return set_date_field(ctx, this_val, 1, args, 0x011);
+}
+
+static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // toJSON(key)
+ JSValue obj, tv, method, rv;
+ double d;
+
+ rv = JS_EXCEPTION;
+ tv = JS_UNDEFINED;
+
+ obj = JS_ToObject(ctx, this_val);
+ tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
+ if (JS_IsException(tv))
+ goto exception;
+ if (JS_IsNumber(tv)) {
+ if (JS_ToFloat64(ctx, &d, tv) < 0)
+ goto exception;
+ if (!isfinite(d)) {
+ rv = JS_NULL;
+ goto done;
+ }
+ }
+ method = JS_GetPropertyStr(ctx, obj, "toISOString");
+ if (JS_IsException(method))
+ goto exception;
+ if (!JS_IsFunction(ctx, method)) {
+ JS_ThrowTypeError(ctx, "object needs toISOString method");
+ JS_FreeValue(ctx, method);
+ goto exception;
+ }
+ rv = JS_CallFree(ctx, method, obj, 0, NULL);
+exception:
+done:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, tv);
+ return rv;
+}
+
+static const JSCFunctionListEntry js_date_funcs[] = {
+ JS_CFUNC_DEF("now", 0, js_Date_now ),
+ JS_CFUNC_DEF("parse", 1, js_Date_parse ),
+ JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
+};
+
+static const JSCFunctionListEntry js_date_proto_funcs[] = {
+ JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
+ JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
+ JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
+ JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
+ JS_ALIAS_DEF("toGMTString", "toUTCString" ),
+ JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
+ JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
+ JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
+ JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
+ JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
+ JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
+ JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
+ JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
+ JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
+ JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
+ JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
+ JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
+ JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
+ JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
+ JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
+ JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
+ JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
+ JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
+ JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
+ JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
+ JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
+ JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
+ JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
+ JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
+ JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
+ JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
+ JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
+ JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
+ JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
+ JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
+ JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
+ JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
+ JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
+};
+
+void JS_AddIntrinsicDate(JSContext *ctx)
+{
+ JSValueConst obj;
+
+ /* Date */
+ ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
+ countof(js_date_proto_funcs));
+ obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
+ ctx->class_proto[JS_CLASS_DATE]);
+ JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
+}
+
+/* eval */
+
+void JS_AddIntrinsicEval(JSContext *ctx)
+{
+ ctx->eval_internal = JS_EvalInternalImpl;
+}
+
+#ifdef CONFIG_BIGNUM
+
+/* Operators */
+
+static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
+ int i, j;
+ JSBinaryOperatorDefEntry *ent;
+
+ if (opset) {
+ for(i = 0; i < JS_OVOP_COUNT; i++) {
+ if (opset->self_ops[i])
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));
+ }
+ for(j = 0; j < opset->left.count; j++) {
+ ent = &opset->left.tab[j];
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ if (ent->ops[i])
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
+ }
+ }
+ js_free_rt(rt, opset->left.tab);
+ for(j = 0; j < opset->right.count; j++) {
+ ent = &opset->right.tab[j];
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ if (ent->ops[i])
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
+ }
+ }
+ js_free_rt(rt, opset->right.tab);
+ js_free_rt(rt, opset);
+ }
+}
+
+static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
+ int i, j;
+ JSBinaryOperatorDefEntry *ent;
+
+ if (opset) {
+ for(i = 0; i < JS_OVOP_COUNT; i++) {
+ if (opset->self_ops[i])
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),
+ mark_func);
+ }
+ for(j = 0; j < opset->left.count; j++) {
+ ent = &opset->left.tab[j];
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ if (ent->ops[i])
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
+ mark_func);
+ }
+ }
+ for(j = 0; j < opset->right.count; j++) {
+ ent = &opset->right.tab[j];
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ if (ent->ops[i])
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
+ mark_func);
+ }
+ }
+ }
+}
+
+
+/* create an OperatorSet object */
+static JSValue js_operators_create_internal(JSContext *ctx,
+ int argc, JSValueConst *argv,
+ BOOL is_primitive)
+{
+ JSValue opset_obj, prop, obj;
+ JSOperatorSetData *opset, *opset1;
+ JSBinaryOperatorDef *def;
+ JSValueConst arg;
+ int i, j;
+ JSBinaryOperatorDefEntry *new_tab;
+ JSBinaryOperatorDefEntry *ent;
+ uint32_t op_count;
+
+ if (ctx->rt->operator_count == UINT32_MAX) {
+ return JS_ThrowTypeError(ctx, "too many operators");
+ }
+ opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);
+ if (JS_IsException(opset_obj))
+ goto fail;
+ opset = js_mallocz(ctx, sizeof(*opset));
+ if (!opset)
+ goto fail;
+ JS_SetOpaque(opset_obj, opset);
+ if (argc >= 1) {
+ arg = argv[0];
+ /* self operators */
+ for(i = 0; i < JS_OVOP_COUNT; i++) {
+ prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);
+ if (JS_IsException(prop))
+ goto fail;
+ if (!JS_IsUndefined(prop)) {
+ if (check_function(ctx, prop)) {
+ JS_FreeValue(ctx, prop);
+ goto fail;
+ }
+ opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);
+ }
+ }
+ }
+ /* left & right operators */
+ for(j = 1; j < argc; j++) {
+ arg = argv[j];
+ prop = JS_GetPropertyStr(ctx, arg, "left");
+ if (JS_IsException(prop))
+ goto fail;
+ def = &opset->right;
+ if (JS_IsUndefined(prop)) {
+ prop = JS_GetPropertyStr(ctx, arg, "right");
+ if (JS_IsException(prop))
+ goto fail;
+ if (JS_IsUndefined(prop)) {
+ JS_ThrowTypeError(ctx, "left or right property must be present");
+ goto fail;
+ }
+ def = &opset->left;
+ }
+ /* get the operator set */
+ obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);
+ JS_FreeValue(ctx, prop);
+ if (JS_IsException(obj))
+ goto fail;
+ prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
+ JS_FreeValue(ctx, obj);
+ if (JS_IsException(prop))
+ goto fail;
+ opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);
+ if (!opset1) {
+ JS_FreeValue(ctx, prop);
+ goto fail;
+ }
+ op_count = opset1->operator_counter;
+ JS_FreeValue(ctx, prop);
+
+ /* we assume there are few entries */
+ new_tab = js_realloc(ctx, def->tab,
+ (def->count + 1) * sizeof(def->tab[0]));
+ if (!new_tab)
+ goto fail;
+ def->tab = new_tab;
+ def->count++;
+ ent = def->tab + def->count - 1;
+ memset(ent, 0, sizeof(def->tab[0]));
+ ent->operator_index = op_count;
+
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ prop = JS_GetPropertyStr(ctx, arg,
+ js_overloadable_operator_names[i]);
+ if (JS_IsException(prop))
+ goto fail;
+ if (!JS_IsUndefined(prop)) {
+ if (check_function(ctx, prop)) {
+ JS_FreeValue(ctx, prop);
+ goto fail;
+ }
+ ent->ops[i] = JS_VALUE_GET_OBJ(prop);
+ }
+ }
+ }
+ opset->is_primitive = is_primitive;
+ opset->operator_counter = ctx->rt->operator_count++;
+ return opset_obj;
+ fail:
+ JS_FreeValue(ctx, opset_obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_operators_create_internal(ctx, argc, argv, FALSE);
+}
+
+static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue opset_obj, prop;
+ JSOperatorSetData *opset;
+ const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };
+ JSOverloadableOperatorEnum op;
+ int i;
+
+ opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
+ JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset_obj))
+ goto fail;
+ opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset)
+ goto fail;
+ for(i = 0; i < countof(ops); i++) {
+ op = ops[i];
+ prop = JS_GetPropertyStr(ctx, argv[0],
+ js_overloadable_operator_names[op]);
+ if (JS_IsException(prop))
+ goto fail;
+ if (!JS_IsUndefined(prop)) {
+ if (!JS_IsNull(prop) && check_function(ctx, prop)) {
+ JS_FreeValue(ctx, prop);
+ goto fail;
+ }
+ if (opset->self_ops[op])
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));
+ if (JS_IsNull(prop)) {
+ opset->self_ops[op] = NULL;
+ } else {
+ opset->self_ops[op] = JS_VALUE_GET_PTR(prop);
+ }
+ }
+ }
+ JS_FreeValue(ctx, opset_obj);
+ return JS_UNDEFINED;
+ fail:
+ JS_FreeValue(ctx, opset_obj);
+ return JS_EXCEPTION;
+}
+
+static int js_operators_set_default(JSContext *ctx, JSValueConst obj)
+{
+ JSValue opset_obj;
+
+ if (!JS_IsObject(obj)) /* in case the prototype is not defined */
+ return 0;
+ opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);
+ if (JS_IsException(opset_obj))
+ return -1;
+ /* cannot be modified by the user */
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,
+ opset_obj, 0);
+ return 0;
+}
+
+static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
+}
+
+static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue func_obj, proto, opset_obj;
+
+ func_obj = JS_UNDEFINED;
+ proto = JS_NewObject(ctx);
+ if (JS_IsException(proto))
+ return JS_EXCEPTION;
+ opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);
+ if (JS_IsException(opset_obj))
+ goto fail;
+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,
+ opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",
+ 0, JS_CFUNC_constructor, 0);
+ if (JS_IsException(func_obj))
+ goto fail;
+ JS_SetConstructor2(ctx, func_obj, proto,
+ 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, proto);
+ return func_obj;
+ fail:
+ JS_FreeValue(ctx, proto);
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_operators_funcs[] = {
+ JS_CFUNC_DEF("create", 1, js_operators_create ),
+ JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),
+};
+
+/* must be called after all overloadable base types are initialized */
+void JS_AddIntrinsicOperators(JSContext *ctx)
+{
+ JSValue obj;
+
+ ctx->allow_operator_overloading = TRUE;
+ obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);
+ JS_SetPropertyFunctionList(ctx, obj,
+ js_operators_funcs,
+ countof(js_operators_funcs));
+ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,
+ obj,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ /* add default operatorSets */
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
+}
+
+/* BigInt */
+
+static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
+{
+ uint32_t tag;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_BIG_INT:
+ break;
+ case JS_TAG_FLOAT64:
+ case JS_TAG_BIG_FLOAT:
+ {
+ bf_t *a, a_s;
+
+ a = JS_ToBigFloat(ctx, &a_s, val);
+ if (!bf_is_finite(a)) {
+ JS_FreeValue(ctx, val);
+ val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");
+ } else {
+ JSValue val1 = JS_NewBigInt(ctx);
+ bf_t *r;
+ int ret;
+ if (JS_IsException(val1)) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigInt(val1);
+ ret = bf_set(r, a);
+ ret |= bf_rint(r, BF_RNDZ);
+ JS_FreeValue(ctx, val);
+ if (ret & BF_ST_MEM_ERROR) {
+ JS_FreeValue(ctx, val1);
+ val = JS_ThrowOutOfMemory(ctx);
+ } else if (ret & BF_ST_INEXACT) {
+ JS_FreeValue(ctx, val1);
+ val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");
+ } else {
+ val = JS_CompactBigInt(ctx, val1);
+ }
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_STRING:
+ val = JS_StringToBigIntErr(ctx, val);
+ break;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ default:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert to bigint");
+ }
+ return val;
+}
+
+static JSValue js_bigint_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ if (!JS_IsUndefined(new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
+}
+
+static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_IsBigInt(ctx, this_val))
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_BIG_INT) {
+ if (JS_IsBigInt(ctx, p->u.object_data))
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a bigint");
+}
+
+static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int base;
+ JSValue ret;
+
+ val = js_thisBigIntValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ base = 10;
+ } else {
+ base = js_get_radix(ctx, argv[0]);
+ if (base < 0)
+ goto fail;
+ }
+ ret = js_bigint_to_string1(ctx, val, base);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisBigIntValue(ctx, this_val);
+}
+
+static JSValue js_bigint_div(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bf_t a_s, b_s, *a, *b, *r, *q;
+ int status;
+ JSValue q_val, r_val;
+
+ q_val = JS_NewBigInt(ctx);
+ if (JS_IsException(q_val))
+ return JS_EXCEPTION;
+ r_val = JS_NewBigInt(ctx);
+ if (JS_IsException(r_val))
+ goto fail;
+ b = NULL;
+ a = JS_ToBigInt(ctx, &a_s, argv[0]);
+ if (!a)
+ goto fail;
+ b = JS_ToBigInt(ctx, &b_s, argv[1]);
+ if (!b) {
+ JS_FreeBigInt(ctx, a, &a_s);
+ goto fail;
+ }
+ q = JS_GetBigInt(q_val);
+ r = JS_GetBigInt(r_val);
+ status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ if (unlikely(status)) {
+ throw_bf_exception(ctx, status);
+ goto fail;
+ }
+ q_val = JS_CompactBigInt(ctx, q_val);
+ if (magic & 0x10) {
+ JSValue ret;
+ ret = JS_NewArray(ctx);
+ if (JS_IsException(ret))
+ goto fail;
+ JS_SetPropertyUint32(ctx, ret, 0, q_val);
+ JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
+ return ret;
+ } else {
+ JS_FreeValue(ctx, r_val);
+ return q_val;
+ }
+ fail:
+ JS_FreeValue(ctx, q_val);
+ JS_FreeValue(ctx, r_val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigint_sqrt(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bf_t a_s, *a, *r, *rem;
+ int status;
+ JSValue r_val, rem_val;
+
+ r_val = JS_NewBigInt(ctx);
+ if (JS_IsException(r_val))
+ return JS_EXCEPTION;
+ rem_val = JS_NewBigInt(ctx);
+ if (JS_IsException(rem_val))
+ return JS_EXCEPTION;
+ r = JS_GetBigInt(r_val);
+ rem = JS_GetBigInt(rem_val);
+
+ a = JS_ToBigInt(ctx, &a_s, argv[0]);
+ if (!a)
+ goto fail;
+ status = bf_sqrtrem(r, rem, a);
+ JS_FreeBigInt(ctx, a, &a_s);
+ if (unlikely(status & ~BF_ST_INEXACT)) {
+ throw_bf_exception(ctx, status);
+ goto fail;
+ }
+ r_val = JS_CompactBigInt(ctx, r_val);
+ if (magic) {
+ JSValue ret;
+ ret = JS_NewArray(ctx);
+ if (JS_IsException(ret))
+ goto fail;
+ JS_SetPropertyUint32(ctx, ret, 0, r_val);
+ JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
+ return ret;
+ } else {
+ JS_FreeValue(ctx, rem_val);
+ return r_val;
+ }
+ fail:
+ JS_FreeValue(ctx, r_val);
+ JS_FreeValue(ctx, rem_val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigint_op1(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic)
+{
+ bf_t a_s, *a;
+ int64_t res;
+
+ a = JS_ToBigInt(ctx, &a_s, argv[0]);
+ if (!a)
+ return JS_EXCEPTION;
+ switch(magic) {
+ case 0: /* floorLog2 */
+ if (a->sign || a->expn <= 0) {
+ res = -1;
+ } else {
+ res = a->expn - 1;
+ }
+ break;
+ case 1: /* ctz */
+ if (bf_is_zero(a)) {
+ res = -1;
+ } else {
+ res = bf_get_exp_min(a);
+ }
+ break;
+ default:
+ abort();
+ }
+ JS_FreeBigInt(ctx, a, &a_s);
+ return JS_NewBigInt64(ctx, res);
+}
+
+static JSValue js_bigint_asUintN(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int asIntN)
+{
+ uint64_t bits;
+ bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
+ JSValue res;
+
+ if (JS_ToIndex(ctx, &bits, argv[0]))
+ return JS_EXCEPTION;
+ res = JS_NewBigInt(ctx);
+ if (JS_IsException(res))
+ return JS_EXCEPTION;
+ r = JS_GetBigInt(res);
+ a = JS_ToBigInt(ctx, &a_s, argv[1]);
+ if (!a) {
+ JS_FreeValue(ctx, res);
+ return JS_EXCEPTION;
+ }
+ /* XXX: optimize */
+ r = JS_GetBigInt(res);
+ bf_init(ctx->bf_ctx, mask);
+ bf_set_ui(mask, 1);
+ bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
+ bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);
+ bf_logic_and(r, a, mask);
+ if (asIntN && bits != 0) {
+ bf_set_ui(mask, 1);
+ bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);
+ if (bf_cmpu(r, mask) >= 0) {
+ bf_set_ui(mask, 1);
+ bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
+ bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);
+ }
+ }
+ bf_delete(mask);
+ JS_FreeBigInt(ctx, a, &a_s);
+ return JS_CompactBigInt(ctx, res);
+}
+
+static const JSCFunctionListEntry js_bigint_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
+ JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
+ /* QuickJS extensions */
+ JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
+ JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
+ JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
+ JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
+ JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
+ JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
+ JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
+ JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
+ JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
+ JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
+ JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
+};
+
+static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
+};
+
+void JS_AddIntrinsicBigInt(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValueConst obj1;
+
+ rt->bigint_ops.to_string = js_bigint_to_string;
+ rt->bigint_ops.from_string = js_string_to_bigint;
+ rt->bigint_ops.unary_arith = js_unary_arith_bigint;
+ rt->bigint_ops.binary_arith = js_binary_arith_bigint;
+ rt->bigint_ops.compare = js_compare_bigfloat;
+
+ ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
+ js_bigint_proto_funcs,
+ countof(js_bigint_proto_funcs));
+ obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
+ ctx->class_proto[JS_CLASS_BIG_INT]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
+ countof(js_bigint_funcs));
+}
+
+/* BigFloat */
+
+static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_IsBigFloat(this_val))
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_BIG_FLOAT) {
+ if (JS_IsBigFloat(p->u.object_data))
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a bigfloat");
+}
+
+static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int base;
+ JSValue ret;
+
+ val = js_thisBigFloatValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ base = 10;
+ } else {
+ base = js_get_radix(ctx, argv[0]);
+ if (base < 0)
+ goto fail;
+ }
+ ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisBigFloatValue(ctx, this_val);
+}
+
+static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
+{
+ int rnd_mode;
+ if (JS_ToInt32Sat(ctx, &rnd_mode, val))
+ return -1;
+ if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {
+ JS_ThrowRangeError(ctx, "invalid rounding mode");
+ return -1;
+ }
+ return rnd_mode;
+}
+
+static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t f;
+ int rnd_mode, radix;
+
+ val = js_thisBigFloatValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToInt64Sat(ctx, &f, argv[0]))
+ goto fail;
+ if (f < 0 || f > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ radix = 10;
+ /* XXX: swap parameter order for rounding mode and radix */
+ if (argc > 1) {
+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ if (argc > 2) {
+ radix = js_get_radix(ctx, argv[2]);
+ if (radix < 0)
+ goto fail;
+ }
+ ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)
+{
+ BOOL res;
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ res = bf_is_finite(&p->num);
+ }
+ break;
+ default:
+ res = FALSE;
+ break;
+ }
+ return res;
+}
+
+static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t f;
+ int rnd_mode, radix;
+
+ val = js_thisBigFloatValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToInt64Sat(ctx, &f, argv[0]))
+ goto fail;
+ if (!js_bigfloat_is_finite(ctx, val)) {
+ ret = JS_ToString(ctx, val);
+ } else if (JS_IsUndefined(argv[0])) {
+ ret = js_ftoa(ctx, val, 10, 0,
+ BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
+ } else {
+ if (f < 0 || f > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ radix = 10;
+ if (argc > 1) {
+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ if (argc > 2) {
+ radix = js_get_radix(ctx, argv[2]);
+ if (radix < 0)
+ goto fail;
+ }
+ ret = js_ftoa(ctx, val, radix, f + 1,
+ rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t p;
+ int rnd_mode, radix;
+
+ val = js_thisBigFloatValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_IsUndefined(argv[0]))
+ goto to_string;
+ if (JS_ToInt64Sat(ctx, &p, argv[0]))
+ goto fail;
+ if (!js_bigfloat_is_finite(ctx, val)) {
+ to_string:
+ ret = JS_ToString(ctx, this_val);
+ } else {
+ if (p < 1 || p > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ radix = 10;
+ if (argc > 1) {
+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ if (argc > 2) {
+ radix = js_get_radix(ctx, argv[2]);
+ if (radix < 0)
+ goto fail;
+ }
+ ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),
+ JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),
+ JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),
+ JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),
+};
+
+static JSValue js_bigfloat_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ if (!JS_IsUndefined(new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (argc == 0) {
+ bf_t *r;
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ return val;
+ r = JS_GetBigFloat(val);
+ bf_set_zero(r, 0);
+ } else {
+ val = JS_DupValue(ctx, argv[0]);
+ redo:
+ switch(JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_BIG_FLOAT:
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ bf_t *r;
+ double d = JS_VALUE_GET_FLOAT64(val);
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigFloat(val);
+ if (bf_set_float64(r, d))
+ goto fail;
+ }
+ break;
+ case JS_TAG_INT:
+ {
+ bf_t *r;
+ int32_t v = JS_VALUE_GET_INT(val);
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigFloat(val);
+ if (bf_set_si(r, v))
+ goto fail;
+ }
+ break;
+ case JS_TAG_BIG_INT:
+ /* We keep the full precision of the integer */
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_STRING:
+ {
+ const char *str, *p;
+ size_t len;
+ int err;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ bf_t *r;
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigFloat(val);
+ bf_set_zero(r, 0);
+ err = 0;
+ } else {
+ val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |
+ ATOD_TYPE_BIG_FLOAT |
+ ATOD_ACCEPT_PREFIX_AFTER_SIGN);
+ if (JS_IsException(val)) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ p += skip_spaces(p);
+ err = ((p - str) != len);
+ }
+ JS_FreeCString(ctx, str);
+ if (err) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");
+ }
+ }
+ break;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ default:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");
+ }
+ }
+ return val;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigfloat_get_const(JSContext *ctx,
+ JSValueConst this_val, int magic)
+{
+ bf_t *r;
+ JSValue val;
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ return val;
+ r = JS_GetBigFloat(val);
+ switch(magic) {
+ case 0: /* PI */
+ bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case 1: /* LN2 */
+ bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case 2: /* MIN_VALUE */
+ case 3: /* MAX_VALUE */
+ {
+ slimb_t e_range, e;
+ e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);
+ bf_set_ui(r, 1);
+ if (magic == 2) {
+ e = -e_range + 2;
+ if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)
+ e -= ctx->fp_env.prec - 1;
+ bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);
+ } else {
+ bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,
+ ctx->fp_env.flags);
+ bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);
+ bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,
+ ctx->fp_env.flags);
+ }
+ }
+ break;
+ case 4: /* EPSILON */
+ bf_set_ui(r, 1);
+ bf_mul_2exp(r, 1 - ctx->fp_env.prec,
+ ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ default:
+ abort();
+ }
+ return val;
+}
+
+static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ bf_t *a;
+ const char *str;
+ JSValue ret;
+ int radix;
+ JSFloatEnv *fe;
+
+ str = JS_ToCString(ctx, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &radix, argv[1])) {
+ fail:
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ if (radix != 0 && (radix < 2 || radix > 36)) {
+ JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
+ goto fail;
+ }
+ fe = &ctx->fp_env;
+ if (argc > 2) {
+ fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ goto fail;
+ }
+ ret = JS_NewBigFloat(ctx);
+ if (JS_IsException(ret))
+ goto done;
+ a = JS_GetBigFloat(ret);
+ /* XXX: use js_atof() */
+ bf_atof(a, str, NULL, radix, fe->prec, fe->flags);
+ done:
+ JS_FreeCString(ctx, str);
+ return ret;
+}
+
+static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst val = argv[0];
+ JSBigFloat *p;
+
+ if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
+ return JS_FALSE;
+ p = JS_VALUE_GET_PTR(val);
+ return JS_NewBool(ctx, bf_is_finite(&p->num));
+}
+
+static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst val = argv[0];
+ JSBigFloat *p;
+
+ if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
+ return JS_FALSE;
+ p = JS_VALUE_GET_PTR(val);
+ return JS_NewBool(ctx, bf_is_nan(&p->num));
+}
+
+enum {
+ MATH_OP_ABS,
+ MATH_OP_FLOOR,
+ MATH_OP_CEIL,
+ MATH_OP_ROUND,
+ MATH_OP_TRUNC,
+ MATH_OP_SQRT,
+ MATH_OP_FPROUND,
+ MATH_OP_ACOS,
+ MATH_OP_ASIN,
+ MATH_OP_ATAN,
+ MATH_OP_ATAN2,
+ MATH_OP_COS,
+ MATH_OP_EXP,
+ MATH_OP_LOG,
+ MATH_OP_POW,
+ MATH_OP_SIN,
+ MATH_OP_TAN,
+ MATH_OP_FMOD,
+ MATH_OP_REM,
+ MATH_OP_SIGN,
+
+ MATH_OP_ADD,
+ MATH_OP_SUB,
+ MATH_OP_MUL,
+ MATH_OP_DIV,
+};
+
+static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bf_t a_s, *a, *r;
+ JSFloatEnv *fe;
+ int rnd_mode;
+ JSValue op1, res;
+
+ op1 = JS_ToNumeric(ctx, argv[0]);
+ if (JS_IsException(op1))
+ return op1;
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ fe = &ctx->fp_env;
+ if (argc > 1) {
+ fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ goto fail;
+ }
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ fail:
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigFloat(res);
+ switch (magic) {
+ case MATH_OP_ABS:
+ bf_set(r, a);
+ r->sign = 0;
+ break;
+ case MATH_OP_FLOOR:
+ rnd_mode = BF_RNDD;
+ goto rint;
+ case MATH_OP_CEIL:
+ rnd_mode = BF_RNDU;
+ goto rint;
+ case MATH_OP_ROUND:
+ rnd_mode = BF_RNDNA;
+ goto rint;
+ case MATH_OP_TRUNC:
+ rnd_mode = BF_RNDZ;
+ rint:
+ bf_set(r, a);
+ fe->status |= bf_rint(r, rnd_mode);
+ break;
+ case MATH_OP_SQRT:
+ fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_FPROUND:
+ bf_set(r, a);
+ fe->status |= bf_round(r, fe->prec, fe->flags);
+ break;
+ case MATH_OP_ACOS:
+ fe->status |= bf_acos(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_ASIN:
+ fe->status |= bf_asin(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_ATAN:
+ fe->status |= bf_atan(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_COS:
+ fe->status |= bf_cos(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_EXP:
+ fe->status |= bf_exp(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_LOG:
+ fe->status |= bf_log(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_SIN:
+ fe->status |= bf_sin(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_TAN:
+ fe->status |= bf_tan(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_SIGN:
+ if (bf_is_nan(a) || bf_is_zero(a)) {
+ bf_set(r, a);
+ } else {
+ bf_set_si(r, 1 - 2 * a->sign);
+ }
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ return res;
+}
+
+static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;
+ JSFloatEnv *fe;
+ JSValue op1, op2, res;
+
+ op1 = JS_ToNumeric(ctx, argv[0]);
+ if (JS_IsException(op1))
+ return op1;
+ op2 = JS_ToNumeric(ctx, argv[1]);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ return op2;
+ }
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ b = JS_ToBigFloat(ctx, &b_s, op2);
+ fe = &ctx->fp_env;
+ if (argc > 2) {
+ fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ goto fail;
+ }
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ fail:
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigFloat(res);
+ switch (magic) {
+ case MATH_OP_ATAN2:
+ fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_POW:
+ fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);
+ break;
+ case MATH_OP_FMOD:
+ fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
+ break;
+ case MATH_OP_REM:
+ fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);
+ break;
+ case MATH_OP_ADD:
+ fe->status |= bf_add(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_SUB:
+ fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_MUL:
+ fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_DIV:
+ fe->status |= bf_div(r, a, b, fe->prec, fe->flags);
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return res;
+}
+
+static const JSCFunctionListEntry js_bigfloat_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),
+ JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),
+ JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),
+ JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),
+ JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),
+ JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),
+ JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),
+ JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),
+ JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),
+ JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),
+ JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),
+ JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),
+ JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),
+ JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),
+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),
+ JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),
+ JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),
+ JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),
+ JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),
+ JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),
+ JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),
+ JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),
+ JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),
+ JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),
+ JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),
+ JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),
+ JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),
+ JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),
+ JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),
+ JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),
+ JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),
+ JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),
+};
+
+/* FloatEnv */
+
+static JSValue js_float_env_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ JSFloatEnv *fe;
+ int64_t prec;
+ int flags, rndmode;
+
+ prec = ctx->fp_env.prec;
+ flags = ctx->fp_env.flags;
+ if (!JS_IsUndefined(argv[0])) {
+ if (JS_ToInt64Sat(ctx, &prec, argv[0]))
+ return JS_EXCEPTION;
+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
+ return JS_ThrowRangeError(ctx, "invalid precision");
+ flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */
+ if (argc > 1 && !JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))
+ return JS_EXCEPTION;
+ if (rndmode < BF_RNDN || rndmode > BF_RNDF)
+ return JS_ThrowRangeError(ctx, "invalid rounding mode");
+ flags = rndmode;
+ }
+ }
+
+ obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ fe = js_malloc(ctx, sizeof(*fe));
+ if (!fe)
+ return JS_EXCEPTION;
+ fe->prec = prec;
+ fe->flags = flags;
+ fe->status = 0;
+ JS_SetOpaque(obj, fe);
+ return obj;
+}
+
+static void js_float_env_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);
+ js_free_rt(rt, fe);
+}
+
+static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)
+{
+ return JS_NewInt64(ctx, ctx->fp_env.prec);
+}
+
+static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
+{
+ return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
+}
+
+static JSValue js_float_env_setPrec(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst func;
+ int exp_bits, flags, saved_flags;
+ JSValue ret;
+ limb_t saved_prec;
+ int64_t prec;
+
+ func = argv[0];
+ if (JS_ToInt64Sat(ctx, &prec, argv[1]))
+ return JS_EXCEPTION;
+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
+ return JS_ThrowRangeError(ctx, "invalid precision");
+ exp_bits = BF_EXP_BITS_MAX;
+
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))
+ return JS_EXCEPTION;
+ if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)
+ return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
+ }
+
+ flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
+
+ saved_prec = ctx->fp_env.prec;
+ saved_flags = ctx->fp_env.flags;
+
+ ctx->fp_env.prec = prec;
+ ctx->fp_env.flags = flags;
+
+ ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
+ /* always restore the floating point precision */
+ ctx->fp_env.prec = saved_prec;
+ ctx->fp_env.flags = saved_flags;
+ return ret;
+}
+
+#define FE_PREC (-1)
+#define FE_EXP (-2)
+#define FE_RNDMODE (-3)
+#define FE_SUBNORMAL (-4)
+
+static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)
+{
+ JSFloatEnv *fe;
+ fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ return JS_EXCEPTION;
+ switch(magic) {
+ case FE_PREC:
+ return JS_NewInt64(ctx, fe->prec);
+ case FE_EXP:
+ return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));
+ case FE_RNDMODE:
+ return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);
+ case FE_SUBNORMAL:
+ return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0);
+ default:
+ return JS_NewBool(ctx, (fe->status & magic) != 0);
+ }
+}
+
+static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)
+{
+ JSFloatEnv *fe;
+ int b;
+ int64_t prec;
+
+ fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ return JS_EXCEPTION;
+ switch(magic) {
+ case FE_PREC:
+ if (JS_ToInt64Sat(ctx, &prec, val))
+ return JS_EXCEPTION;
+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
+ return JS_ThrowRangeError(ctx, "invalid precision");
+ fe->prec = prec;
+ break;
+ case FE_EXP:
+ if (JS_ToInt32Sat(ctx, &b, val))
+ return JS_EXCEPTION;
+ if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
+ return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
+ fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
+ bf_set_exp_bits(b);
+ break;
+ case FE_RNDMODE:
+ b = bigfloat_get_rnd_mode(ctx, val);
+ if (b < 0)
+ return JS_EXCEPTION;
+ fe->flags = (fe->flags & ~BF_RND_MASK) | b;
+ break;
+ case FE_SUBNORMAL:
+ b = JS_ToBool(ctx, val);
+ fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
+ break;
+ default:
+ b = JS_ToBool(ctx, val);
+ fe->status = (fe->status & ~magic) & ((-b) & magic);
+ break;
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_float_env_clearStatus(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ return JS_EXCEPTION;
+ fe->status = 0;
+ return JS_UNDEFINED;
+}
+
+static const JSCFunctionListEntry js_float_env_funcs[] = {
+ JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),
+ JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),
+ JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),
+ JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),
+ JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),
+ JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),
+ JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),
+ JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),
+ JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),
+ JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),
+ JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),
+ JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),
+ JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),
+ JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
+};
+
+static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, FE_PREC ),
+ JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, FE_EXP ),
+ JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, FE_RNDMODE ),
+ JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, FE_SUBNORMAL ),
+ JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_INVALID_OP ),
+ JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),
+ JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_OVERFLOW ),
+ JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_UNDERFLOW ),
+ JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_INEXACT ),
+ JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),
+};
+
+void JS_AddIntrinsicBigFloat(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValueConst obj1;
+
+ rt->bigfloat_ops.to_string = js_bigfloat_to_string;
+ rt->bigfloat_ops.from_string = js_string_to_bigfloat;
+ rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;
+ rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;
+ rt->bigfloat_ops.compare = js_compare_bigfloat;
+ rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;
+ rt->bigfloat_ops.mul_pow10 = js_mul_pow10;
+
+ ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],
+ js_bigfloat_proto_funcs,
+ countof(js_bigfloat_proto_funcs));
+ obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,
+ ctx->class_proto[JS_CLASS_BIG_FLOAT]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,
+ countof(js_bigfloat_funcs));
+
+ ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],
+ js_float_env_proto_funcs,
+ countof(js_float_env_proto_funcs));
+ obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",
+ js_float_env_constructor, 1,
+ ctx->class_proto[JS_CLASS_FLOAT_ENV]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,
+ countof(js_float_env_funcs));
+}
+
+/* BigDecimal */
+
+static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
+ BOOL allow_null_or_undefined)
+{
+ redo:
+ switch(JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_BIG_DECIMAL:
+ break;
+ case JS_TAG_NULL:
+ if (!allow_null_or_undefined)
+ goto fail;
+ /* fall thru */
+ case JS_TAG_BOOL:
+ case JS_TAG_INT:
+ {
+ bfdec_t *r;
+ int32_t v = JS_VALUE_GET_INT(val);
+
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigDecimal(val);
+ if (bfdec_set_si(r, v)) {
+ JS_FreeValue(ctx, val);
+ val = JS_EXCEPTION;
+ break;
+ }
+ }
+ break;
+ case JS_TAG_FLOAT64:
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_STRING:
+ {
+ const char *str, *p;
+ size_t len;
+ int err;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ bfdec_t *r;
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigDecimal(val);
+ bfdec_set_zero(r, 0);
+ err = 0;
+ } else {
+ val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
+ if (JS_IsException(val)) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ p += skip_spaces(p);
+ err = ((p - str) != len);
+ }
+ JS_FreeCString(ctx, str);
+ if (err) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");
+ }
+ }
+ break;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_UNDEFINED:
+ {
+ bfdec_t *r;
+ if (!allow_null_or_undefined)
+ goto fail;
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigDecimal(val);
+ bfdec_set_nan(r);
+ }
+ break;
+ default:
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
+ }
+ return val;
+}
+
+static JSValue js_bigdecimal_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ if (!JS_IsUndefined(new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (argc == 0) {
+ bfdec_t *r;
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ return val;
+ r = JS_GetBigDecimal(val);
+ bfdec_set_zero(r, 0);
+ } else {
+ val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
+ }
+ return val;
+}
+
+static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_IsBigDecimal(this_val))
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_BIG_DECIMAL) {
+ if (JS_IsBigDecimal(p->u.object_data))
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a bigdecimal");
+}
+
+static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+
+ val = js_thisBigDecimalValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ return JS_ToStringFree(ctx, val);
+}
+
+static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisBigDecimalValue(ctx, this_val);
+}
+
+static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
+{
+ const char *str;
+ size_t size;
+ int rnd_mode;
+
+ str = JS_ToCStringLen(ctx, &size, obj);
+ if (!str)
+ return -1;
+ if (strlen(str) != size)
+ goto invalid_rounding_mode;
+ if (!strcmp(str, "floor")) {
+ rnd_mode = BF_RNDD;
+ } else if (!strcmp(str, "ceiling")) {
+ rnd_mode = BF_RNDU;
+ } else if (!strcmp(str, "down")) {
+ rnd_mode = BF_RNDZ;
+ } else if (!strcmp(str, "up")) {
+ rnd_mode = BF_RNDA;
+ } else if (!strcmp(str, "half-even")) {
+ rnd_mode = BF_RNDN;
+ } else if (!strcmp(str, "half-up")) {
+ rnd_mode = BF_RNDNA;
+ } else {
+ invalid_rounding_mode:
+ JS_FreeCString(ctx, str);
+ JS_ThrowTypeError(ctx, "invalid rounding mode");
+ return -1;
+ }
+ JS_FreeCString(ctx, str);
+ return rnd_mode;
+}
+
+typedef struct {
+ int64_t prec;
+ bf_flags_t flags;
+} BigDecimalEnv;
+
+static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
+ JSValueConst obj)
+{
+ JSValue prop;
+ int64_t val;
+ BOOL has_prec;
+ int rnd_mode;
+
+ if (!JS_IsObject(obj)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
+ if (JS_IsException(prop))
+ return -1;
+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
+ JS_FreeValue(ctx, prop);
+ if (rnd_mode < 0)
+ return -1;
+ fe->flags = rnd_mode;
+
+ prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
+ if (JS_IsException(prop))
+ return -1;
+ has_prec = FALSE;
+ if (!JS_IsUndefined(prop)) {
+ if (JS_ToInt64SatFree(ctx, &val, prop))
+ return -1;
+ if (val < 1 || val > BF_PREC_MAX)
+ goto invalid_precision;
+ fe->prec = val;
+ has_prec = TRUE;
+ }
+
+ prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
+ if (JS_IsException(prop))
+ return -1;
+ if (!JS_IsUndefined(prop)) {
+ if (has_prec) {
+ JS_FreeValue(ctx, prop);
+ JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
+ return -1;
+ }
+ if (JS_ToInt64SatFree(ctx, &val, prop))
+ return -1;
+ if (val < 0 || val > BF_PREC_MAX) {
+ invalid_precision:
+ JS_ThrowTypeError(ctx, "invalid precision");
+ return -1;
+ }
+ fe->prec = val;
+ fe->flags |= BF_FLAG_RADPNT_PREC;
+ has_prec = TRUE;
+ }
+ if (!has_prec) {
+ JS_ThrowTypeError(ctx, "precision must be present");
+ return -1;
+ }
+ return 0;
+}
+
+
+static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bfdec_t *a, *b, r_s, *r = &r_s;
+ JSValue op1, op2, res;
+ BigDecimalEnv fe_s, *fe = &fe_s;
+ int op_count, ret;
+
+ if (magic == MATH_OP_SQRT ||
+ magic == MATH_OP_ROUND)
+ op_count = 1;
+ else
+ op_count = 2;
+
+ op1 = JS_ToNumeric(ctx, argv[0]);
+ if (JS_IsException(op1))
+ return op1;
+ a = JS_ToBigDecimal(ctx, op1);
+ if (!a) {
+ JS_FreeValue(ctx, op1);
+ return JS_EXCEPTION;
+ }
+ if (op_count >= 2) {
+ op2 = JS_ToNumeric(ctx, argv[1]);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ return op2;
+ }
+ b = JS_ToBigDecimal(ctx, op2);
+ if (!b)
+ goto fail;
+ } else {
+ op2 = JS_UNDEFINED;
+ b = NULL;
+ }
+ fe->flags = BF_RNDZ;
+ fe->prec = BF_PREC_INF;
+ if (op_count < argc) {
+ if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
+ goto fail;
+ }
+
+ res = JS_NewBigDecimal(ctx);
+ if (JS_IsException(res)) {
+ fail:
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigDecimal(res);
+ switch (magic) {
+ case MATH_OP_ADD:
+ ret = bfdec_add(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_SUB:
+ ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_MUL:
+ ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_DIV:
+ ret = bfdec_div(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_FMOD:
+ ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
+ break;
+ case MATH_OP_SQRT:
+ ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_ROUND:
+ ret = bfdec_set(r, a);
+ if (!(ret & BF_ST_MEM_ERROR))
+ ret = bfdec_round(r, fe->prec, fe->flags);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
+ BF_ST_OVERFLOW;
+ if (ret != 0) {
+ JS_FreeValue(ctx, res);
+ return throw_bf_exception(ctx, ret);
+ } else {
+ return res;
+ }
+}
+
+static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t f;
+ int rnd_mode;
+
+ val = js_thisBigDecimalValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToInt64Sat(ctx, &f, argv[0]))
+ goto fail;
+ if (f < 0 || f > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ if (argc > 1) {
+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t f;
+ int rnd_mode;
+
+ val = js_thisBigDecimalValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToInt64Sat(ctx, &f, argv[0]))
+ goto fail;
+ if (JS_IsUndefined(argv[0])) {
+ ret = js_bigdecimal_to_string1(ctx, val, 0,
+ BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
+ } else {
+ if (f < 0 || f > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ if (argc > 1) {
+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ ret = js_bigdecimal_to_string1(ctx, val, f + 1,
+ rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t p;
+ int rnd_mode;
+
+ val = js_thisBigDecimalValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_IsUndefined(argv[0])) {
+ return JS_ToStringFree(ctx, val);
+ }
+ if (JS_ToInt64Sat(ctx, &p, argv[0]))
+ goto fail;
+ if (p < 1 || p > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ if (argc > 1) {
+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ ret = js_bigdecimal_to_string1(ctx, val, p,
+ rnd_mode | BF_FTOA_FORMAT_FIXED);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
+ JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
+ JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
+ JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
+};
+
+static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
+ JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
+ JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
+ JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
+ JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
+ JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
+};
+
+void JS_AddIntrinsicBigDecimal(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValueConst obj1;
+
+ rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
+ rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
+ rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
+ rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
+ rt->bigdecimal_ops.compare = js_compare_bigdecimal;
+
+ ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
+ js_bigdecimal_proto_funcs,
+ countof(js_bigdecimal_proto_funcs));
+ obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
+ js_bigdecimal_constructor, 1,
+ ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
+ countof(js_bigdecimal_funcs));
+}
+
+void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
+{
+ ctx->bignum_ext = enable;
+}
+
+#endif /* CONFIG_BIGNUM */
+
+static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
+ "EvalError", "RangeError", "ReferenceError",
+ "SyntaxError", "TypeError", "URIError",
+ "InternalError", "AggregateError",
+};
+
+/* Minimum amount of objects to be able to compile code and display
+ error messages. No JSAtom should be allocated by this function. */
+static void JS_AddIntrinsicBasicObjects(JSContext *ctx)
+{
+ JSValue proto;
+ int i;
+
+ ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);
+ ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,
+ JS_CFUNC_generic, 0,
+ ctx->class_proto[JS_CLASS_OBJECT]);
+ ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);
+ ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);
+#if 0
+ /* these are auto-initialized from js_error_proto_funcs,
+ but delaying might be a problem */
+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,
+ JS_AtomToString(ctx, JS_ATOM_Error),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,
+ JS_AtomToString(ctx, JS_ATOM_empty_string),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+#endif
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],
+ js_error_proto_funcs,
+ countof(js_error_proto_funcs));
+
+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
+ proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);
+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,
+ JS_NewAtomString(ctx, native_error_name[i]),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,
+ JS_AtomToString(ctx, JS_ATOM_empty_string),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ ctx->native_error_proto[i] = proto;
+ }
+
+ /* the array prototype is an array */
+ ctx->class_proto[JS_CLASS_ARRAY] =
+ JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_ARRAY);
+
+ ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
+ JS_PROP_INITIAL_HASH_SIZE, 1);
+ add_shape_property(ctx, &ctx->array_shape, NULL,
+ JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);
+
+ /* XXX: could test it on first context creation to ensure that no
+ new atoms are created in JS_AddIntrinsicBasicObjects(). It is
+ necessary to avoid useless renumbering of atoms after
+ JS_EvalBinary() if it is done just after
+ JS_AddIntrinsicBasicObjects(). */
+ // assert(ctx->rt->atom_count == JS_ATOM_END);
+}
+
+void JS_AddIntrinsicBaseObjects(JSContext *ctx)
+{
+ int i;
+ JSValueConst obj, number_obj;
+ JSValue obj1;
+
+ ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);
+
+ /* add caller and arguments properties to throw a TypeError */
+ obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);
+ JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,
+ obj1, ctx->throw_type_error,
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
+ JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,
+ obj1, ctx->throw_type_error,
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+ JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, &ctx->throw_type_error, 1));
+
+ ctx->global_obj = JS_NewObject(ctx);
+ ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);
+
+ /* Object */
+ obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,
+ ctx->class_proto[JS_CLASS_OBJECT]);
+ JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ js_object_proto_funcs, countof(js_object_proto_funcs));
+
+ /* Function */
+ JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));
+ ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,
+ "Function", 1, JS_CFUNC_constructor_or_func_magic,
+ JS_FUNC_NORMAL);
+ JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",
+ ctx->function_proto);
+
+ /* Error */
+ obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,
+ "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
+ JS_NewGlobalCConstructor2(ctx, obj1,
+ "Error", ctx->class_proto[JS_CLASS_ERROR]);
+
+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
+ JSValue func_obj;
+ int n_args;
+ n_args = 1 + (i == JS_AGGREGATE_ERROR);
+ func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor,
+ native_error_name[i], n_args,
+ JS_CFUNC_constructor_or_func_magic, i, obj1);
+ JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],
+ ctx->native_error_proto[i]);
+ }
+
+ /* Iterator prototype */
+ ctx->iterator_proto = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
+ js_iterator_proto_funcs,
+ countof(js_iterator_proto_funcs));
+
+ /* Array */
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],
+ js_array_proto_funcs,
+ countof(js_array_proto_funcs));
+
+ obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,
+ ctx->class_proto[JS_CLASS_ARRAY]);
+ ctx->array_ctor = JS_DupValue(ctx, obj);
+ JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,
+ countof(js_array_funcs));
+
+ /* XXX: create auto_initializer */
+ {
+ /* initialize Array.prototype[Symbol.unscopables] */
+ char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0"
+ "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0";
+ const char *p = unscopables;
+ obj1 = JS_NewObjectProto(ctx, JS_NULL);
+ for(p = unscopables; *p; p += strlen(p) + 1) {
+ JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);
+ }
+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],
+ JS_ATOM_Symbol_unscopables, obj1,
+ JS_PROP_CONFIGURABLE);
+ }
+
+ /* needed to initialize arguments[Symbol.iterator] */
+ ctx->array_proto_values =
+ JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);
+
+ ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],
+ js_array_iterator_proto_funcs,
+ countof(js_array_iterator_proto_funcs));
+
+ /* parseFloat and parseInteger must be defined before Number
+ because of the Number.parseFloat and Number.parseInteger
+ aliases */
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,
+ countof(js_global_funcs));
+
+ /* Number */
+ ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_NUMBER);
+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],
+ js_number_proto_funcs,
+ countof(js_number_proto_funcs));
+ number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,
+ ctx->class_proto[JS_CLASS_NUMBER]);
+ JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));
+
+ /* Boolean */
+ ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_BOOLEAN);
+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,
+ countof(js_boolean_proto_funcs));
+ JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,
+ ctx->class_proto[JS_CLASS_BOOLEAN]);
+
+ /* String */
+ ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_STRING);
+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));
+ obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,
+ ctx->class_proto[JS_CLASS_STRING]);
+ JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,
+ countof(js_string_funcs));
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,
+ countof(js_string_proto_funcs));
+
+ ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],
+ js_string_iterator_proto_funcs,
+ countof(js_string_iterator_proto_funcs));
+
+ /* Math: create as autoinit object */
+ js_random_init(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));
+
+ /* ES6 Reflect: create as autoinit object */
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));
+
+ /* ES6 Symbol */
+ ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,
+ countof(js_symbol_proto_funcs));
+ obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,
+ ctx->class_proto[JS_CLASS_SYMBOL]);
+ JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,
+ countof(js_symbol_funcs));
+ for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ const char *str, *p;
+ str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);
+ /* skip "Symbol." */
+ p = strchr(str, '.');
+ if (p)
+ str = p + 1;
+ JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);
+ }
+
+ /* ES6 Generator */
+ ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],
+ js_generator_proto_funcs,
+ countof(js_generator_proto_funcs));
+
+ ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
+ obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,
+ "GeneratorFunction", 1,
+ JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);
+ JS_SetPropertyFunctionList(ctx,
+ ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
+ js_generator_function_proto_funcs,
+ countof(js_generator_function_proto_funcs));
+ JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
+ ctx->class_proto[JS_CLASS_GENERATOR],
+ JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
+ 0, JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+
+ /* global properties */
+ ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);
+ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,
+ JS_DupValue(ctx, ctx->eval_obj),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,
+ JS_DupValue(ctx, ctx->global_obj),
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
+}
+
+/* Typed Arrays */
+
+static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {
+ 0, 0, 0, 1, 1, 2, 2,
+#ifdef CONFIG_BIGNUM
+ 3, 3, /* BigInt64Array, BigUint64Array */
+#endif
+ 2, 3
+};
+
+static JSValue js_array_buffer_constructor3(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len, JSClassID class_id,
+ uint8_t *buf,
+ JSFreeArrayBufferDataFunc *free_func,
+ void *opaque, BOOL alloc_flag)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValue obj;
+ JSArrayBuffer *abuf = NULL;
+
+ obj = js_create_from_ctor(ctx, new_target, class_id);
+ if (JS_IsException(obj))
+ return obj;
+ /* XXX: we are currently limited to 2 GB */
+ if (len > INT32_MAX) {
+ JS_ThrowRangeError(ctx, "invalid array buffer length");
+ goto fail;
+ }
+ abuf = js_malloc(ctx, sizeof(*abuf));
+ if (!abuf)
+ goto fail;
+ abuf->byte_length = len;
+ if (alloc_flag) {
+ if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
+ rt->sab_funcs.sab_alloc) {
+ abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,
+ max_int(len, 1));
+ if (!abuf->data)
+ goto fail;
+ memset(abuf->data, 0, len);
+ } else {
+ /* the allocation must be done after the object creation */
+ abuf->data = js_mallocz(ctx, max_int(len, 1));
+ if (!abuf->data)
+ goto fail;
+ }
+ } else {
+ if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
+ rt->sab_funcs.sab_dup) {
+ rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);
+ }
+ abuf->data = buf;
+ }
+ init_list_head(&abuf->array_list);
+ abuf->detached = FALSE;
+ abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);
+ abuf->opaque = opaque;
+ abuf->free_func = free_func;
+ if (alloc_flag && buf)
+ memcpy(abuf->data, buf, len);
+ JS_SetOpaque(obj, abuf);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ js_free(ctx, abuf);
+ return JS_EXCEPTION;
+}
+
+static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)
+{
+ js_free_rt(rt, ptr);
+}
+
+static JSValue js_array_buffer_constructor2(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len, JSClassID class_id)
+{
+ return js_array_buffer_constructor3(ctx, new_target, len, class_id,
+ NULL, js_array_buffer_free, NULL,
+ TRUE);
+}
+
+static JSValue js_array_buffer_constructor1(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len)
+{
+ return js_array_buffer_constructor2(ctx, new_target, len,
+ JS_CLASS_ARRAY_BUFFER);
+}
+
+JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
+ JSFreeArrayBufferDataFunc *free_func, void *opaque,
+ BOOL is_shared)
+{
+ return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
+ is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,
+ buf, free_func, opaque, FALSE);
+}
+
+/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */
+JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
+{
+ return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
+ JS_CLASS_ARRAY_BUFFER,
+ (uint8_t *)buf,
+ js_array_buffer_free, NULL,
+ TRUE);
+}
+
+static JSValue js_array_buffer_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ uint64_t len;
+ if (JS_ToIndex(ctx, &len, argv[0]))
+ return JS_EXCEPTION;
+ return js_array_buffer_constructor1(ctx, new_target, len);
+}
+
+static JSValue js_shared_array_buffer_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ uint64_t len;
+ if (JS_ToIndex(ctx, &len, argv[0]))
+ return JS_EXCEPTION;
+ return js_array_buffer_constructor2(ctx, new_target, len,
+ JS_CLASS_SHARED_ARRAY_BUFFER);
+}
+
+/* also used for SharedArrayBuffer */
+static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ if (abuf) {
+ /* The ArrayBuffer finalizer may be called before the typed
+ array finalizers using it, so abuf->array_list is not
+ necessarily empty. */
+ // assert(list_empty(&abuf->array_list));
+ if (abuf->shared && rt->sab_funcs.sab_free) {
+ rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
+ } else {
+ if (abuf->free_func)
+ abuf->free_func(rt, abuf->opaque, abuf->data);
+ }
+ js_free_rt(rt, abuf);
+ }
+}
+
+static JSValue js_array_buffer_isView(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ BOOL res;
+ res = FALSE;
+ if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(argv[0]);
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_DATAVIEW) {
+ res = TRUE;
+ }
+ }
+ return JS_NewBool(ctx, res);
+}
+
+static const JSCFunctionListEntry js_array_buffer_funcs[] = {
+ JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+};
+
+static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
+}
+
+static JSValue js_array_buffer_get_byteLength(JSContext *ctx,
+ JSValueConst this_val,
+ int class_id)
+{
+ JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
+ if (!abuf)
+ return JS_EXCEPTION;
+ /* return 0 if detached */
+ return JS_NewUint32(ctx, abuf->byte_length);
+}
+
+void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
+{
+ JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
+ struct list_head *el;
+
+ if (!abuf || abuf->detached)
+ return;
+ if (abuf->free_func)
+ abuf->free_func(ctx->rt, abuf->opaque, abuf->data);
+ abuf->data = NULL;
+ abuf->byte_length = 0;
+ abuf->detached = TRUE;
+
+ list_for_each(el, &abuf->array_list) {
+ JSTypedArray *ta;
+ JSObject *p;
+
+ ta = list_entry(el, JSTypedArray, link);
+ p = ta->obj;
+ /* Note: the typed array length and offset fields are not modified */
+ if (p->class_id != JS_CLASS_DATAVIEW) {
+ p->u.array.count = 0;
+ p->u.array.u.ptr = NULL;
+ }
+ }
+}
+
+/* get an ArrayBuffer or SharedArrayBuffer */
+static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ goto fail;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id != JS_CLASS_ARRAY_BUFFER &&
+ p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {
+ fail:
+ JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER);
+ return NULL;
+ }
+ return p->u.array_buffer;
+}
+
+/* return NULL if exception. WARNING: any JS call can detach the
+ buffer and render the returned pointer invalid */
+uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj)
+{
+ JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj);
+ if (!abuf)
+ goto fail;
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ *psize = abuf->byte_length;
+ return abuf->data;
+ fail:
+ *psize = 0;
+ return NULL;
+}
+
+static JSValue js_array_buffer_slice(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int class_id)
+{
+ JSArrayBuffer *abuf, *new_abuf;
+ int64_t len, start, end, new_len;
+ JSValue ctor, new_obj;
+
+ abuf = JS_GetOpaque2(ctx, this_val, class_id);
+ if (!abuf)
+ return JS_EXCEPTION;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ len = abuf->byte_length;
+
+ if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
+ return JS_EXCEPTION;
+
+ end = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len))
+ return JS_EXCEPTION;
+ }
+ new_len = max_int64(end - start, 0);
+ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ return ctor;
+ if (JS_IsUndefined(ctor)) {
+ new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,
+ class_id);
+ } else {
+ JSValue args[1];
+ args[0] = JS_NewInt64(ctx, new_len);
+ new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args);
+ JS_FreeValue(ctx, ctor);
+ JS_FreeValue(ctx, args[0]);
+ }
+ if (JS_IsException(new_obj))
+ return new_obj;
+ new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);
+ if (!new_abuf)
+ goto fail;
+ if (js_same_value(ctx, new_obj, this_val)) {
+ JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");
+ goto fail;
+ }
+ if (new_abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ if (new_abuf->byte_length < new_len) {
+ JS_ThrowTypeError(ctx, "new ArrayBuffer is too small");
+ goto fail;
+ }
+ /* must test again because of side effects */
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ memcpy(new_abuf->data, abuf->data + start, new_len);
+ return new_obj;
+ fail:
+ JS_FreeValue(ctx, new_obj);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),
+};
+
+/* SharedArrayBuffer */
+
+static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = {
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+};
+
+static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),
+};
+
+static JSObject *get_typed_array(JSContext *ctx,
+ JSValueConst this_val,
+ int is_dataview)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ goto fail;
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (is_dataview) {
+ if (p->class_id != JS_CLASS_DATAVIEW)
+ goto fail;
+ } else {
+ if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {
+ fail:
+ JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray");
+ return NULL;
+ }
+ }
+ return p;
+}
+
+/* WARNING: 'p' must be a typed array */
+static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p)
+{
+ JSTypedArray *ta = p->u.typed_array;
+ JSArrayBuffer *abuf = ta->buffer->u.array_buffer;
+ /* XXX: could simplify test by ensuring that
+ p->u.array.u.ptr is NULL iff it is detached */
+ return abuf->detached;
+}
+
+/* WARNING: 'p' must be a typed array. Works even if the array buffer
+ is detached */
+static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p)
+{
+ JSTypedArray *ta = p->u.typed_array;
+ int size_log2 = typed_array_size_log2(p->class_id);
+ return ta->length >> size_log2;
+}
+
+static int validate_typed_array(JSContext *ctx, JSValueConst this_val)
+{
+ JSObject *p;
+ p = get_typed_array(ctx, this_val, 0);
+ if (!p)
+ return -1;
+ if (typed_array_is_detached(ctx, p)) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ return -1;
+ }
+ return 0;
+}
+
+static JSValue js_typed_array_get_length(JSContext *ctx,
+ JSValueConst this_val)
+{
+ JSObject *p;
+ p = get_typed_array(ctx, this_val, 0);
+ if (!p)
+ return JS_EXCEPTION;
+ return JS_NewInt32(ctx, p->u.array.count);
+}
+
+static JSValue js_typed_array_get_buffer(JSContext *ctx,
+ JSValueConst this_val, int is_dataview)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ p = get_typed_array(ctx, this_val, is_dataview);
+ if (!p)
+ return JS_EXCEPTION;
+ ta = p->u.typed_array;
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
+}
+
+static JSValue js_typed_array_get_byteLength(JSContext *ctx,
+ JSValueConst this_val,
+ int is_dataview)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ p = get_typed_array(ctx, this_val, is_dataview);
+ if (!p)
+ return JS_EXCEPTION;
+ if (typed_array_is_detached(ctx, p)) {
+ if (is_dataview) {
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ } else {
+ return JS_NewInt32(ctx, 0);
+ }
+ }
+ ta = p->u.typed_array;
+ return JS_NewInt32(ctx, ta->length);
+}
+
+static JSValue js_typed_array_get_byteOffset(JSContext *ctx,
+ JSValueConst this_val,
+ int is_dataview)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ p = get_typed_array(ctx, this_val, is_dataview);
+ if (!p)
+ return JS_EXCEPTION;
+ if (typed_array_is_detached(ctx, p)) {
+ if (is_dataview) {
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ } else {
+ return JS_NewInt32(ctx, 0);
+ }
+ }
+ ta = p->u.typed_array;
+ return JS_NewInt32(ctx, ta->offset);
+}
+
+/* Return the buffer associated to the typed array or an exception if
+ it is not a typed array or if the buffer is detached. pbyte_offset,
+ pbyte_length or pbytes_per_element can be NULL. */
+JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
+ size_t *pbyte_offset,
+ size_t *pbyte_length,
+ size_t *pbytes_per_element)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ p = get_typed_array(ctx, obj, FALSE);
+ if (!p)
+ return JS_EXCEPTION;
+ if (typed_array_is_detached(ctx, p))
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ ta = p->u.typed_array;
+ if (pbyte_offset)
+ *pbyte_offset = ta->offset;
+ if (pbyte_length)
+ *pbyte_length = ta->length;
+ if (pbytes_per_element) {
+ *pbytes_per_element = 1 << typed_array_size_log2(p->class_id);
+ }
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
+}
+
+static JSValue js_typed_array_get_toStringTag(JSContext *ctx,
+ JSValueConst this_val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ return JS_UNDEFINED;
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY))
+ return JS_UNDEFINED;
+ return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name);
+}
+
+static JSValue js_typed_array_set_internal(JSContext *ctx,
+ JSValueConst dst,
+ JSValueConst src,
+ JSValueConst off)
+{
+ JSObject *p;
+ JSObject *src_p;
+ uint32_t i;
+ int64_t src_len, offset;
+ JSValue val, src_obj = JS_UNDEFINED;
+
+ p = get_typed_array(ctx, dst, 0);
+ if (!p)
+ goto fail;
+ if (JS_ToInt64Sat(ctx, &offset, off))
+ goto fail;
+ if (offset < 0)
+ goto range_error;
+ if (typed_array_is_detached(ctx, p)) {
+ detached:
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ src_obj = JS_ToObject(ctx, src);
+ if (JS_IsException(src_obj))
+ goto fail;
+ src_p = JS_VALUE_GET_OBJ(src_obj);
+ if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ JSTypedArray *dest_ta = p->u.typed_array;
+ JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer;
+ JSTypedArray *src_ta = src_p->u.typed_array;
+ JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer;
+ int shift = typed_array_size_log2(p->class_id);
+
+ if (src_abuf->detached)
+ goto detached;
+
+ src_len = src_p->u.array.count;
+ if (offset > (int64_t)(p->u.array.count - src_len))
+ goto range_error;
+
+ /* copying between typed objects */
+ if (src_p->class_id == p->class_id) {
+ /* same type, use memmove */
+ memmove(dest_abuf->data + dest_ta->offset + (offset << shift),
+ src_abuf->data + src_ta->offset, src_len << shift);
+ goto done;
+ }
+ if (dest_abuf->data == src_abuf->data) {
+ /* copying between the same buffer using different types of mappings
+ would require a temporary buffer */
+ }
+ /* otherwise, default behavior is slow but correct */
+ } else {
+ if (js_get_length64(ctx, &src_len, src_obj))
+ goto fail;
+ if (offset > (int64_t)(p->u.array.count - src_len)) {
+ range_error:
+ JS_ThrowRangeError(ctx, "invalid array length");
+ goto fail;
+ }
+ }
+ for(i = 0; i < src_len; i++) {
+ val = JS_GetPropertyUint32(ctx, src_obj, i);
+ if (JS_IsException(val))
+ goto fail;
+ if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0)
+ goto fail;
+ }
+done:
+ JS_FreeValue(ctx, src_obj);
+ return JS_UNDEFINED;
+fail:
+ JS_FreeValue(ctx, src_obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_set(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst offset = JS_UNDEFINED;
+ if (argc > 1) {
+ offset = argv[1];
+ }
+ return js_typed_array_set_internal(ctx, this_val, argv[0], offset);
+}
+
+static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ if (validate_typed_array(ctx, this_val))
+ return JS_EXCEPTION;
+ return js_create_array_iterator(ctx, this_val, argc, argv, magic);
+}
+
+/* return < 0 if exception */
+static int js_typed_array_get_length_internal(JSContext *ctx,
+ JSValueConst obj)
+{
+ JSObject *p;
+ p = get_typed_array(ctx, obj, 0);
+ if (!p)
+ return -1;
+ if (typed_array_is_detached(ctx, p)) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ return -1;
+ }
+ return p->u.array.count;
+}
+
+#if 0
+/* validate a typed array and return its length */
+static JSValue js_typed_array___getLength(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ BOOL ignore_detached = JS_ToBool(ctx, argv[1]);
+
+ if (ignore_detached) {
+ return js_typed_array_get_length(ctx, argv[0]);
+ } else {
+ int len;
+ len = js_typed_array_get_length_internal(ctx, argv[0]);
+ if (len < 0)
+ return JS_EXCEPTION;
+ return JS_NewInt32(ctx, len);
+ }
+}
+#endif
+
+static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,
+ int argc, JSValueConst *argv)
+{
+ JSValue ret;
+ int new_len;
+ int64_t len;
+
+ ret = JS_CallConstructor(ctx, ctor, argc, argv);
+ if (JS_IsException(ret))
+ return ret;
+ /* validate the typed array */
+ new_len = js_typed_array_get_length_internal(ctx, ret);
+ if (new_len < 0)
+ goto fail;
+ if (argc == 1) {
+ /* ensure that it is large enough */
+ if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))
+ goto fail;
+ if (new_len < len) {
+ JS_ThrowTypeError(ctx, "TypedArray length is too small");
+ fail:
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ }
+ return ret;
+}
+
+#if 0
+static JSValue js_typed_array___create(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1);
+}
+#endif
+
+static JSValue js_typed_array___speciesCreate(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj;
+ JSObject *p;
+ JSValue ctor, ret;
+ int argc1;
+
+ obj = argv[0];
+ p = get_typed_array(ctx, obj, 0);
+ if (!p)
+ return JS_EXCEPTION;
+ ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ return ctor;
+ argc1 = max_int(argc - 1, 0);
+ if (JS_IsUndefined(ctor)) {
+ ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1,
+ p->class_id);
+ } else {
+ ret = js_typed_array_create(ctx, ctor, argc1, argv + 1);
+ JS_FreeValue(ctx, ctor);
+ }
+ return ret;
+}
+
+static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // from(items, mapfn = void 0, this_arg = void 0)
+ JSValueConst items = argv[0], mapfn, this_arg;
+ JSValueConst args[2];
+ JSValue stack[2];
+ JSValue iter, arr, r, v, v2;
+ int64_t k, len;
+ int done, mapping;
+
+ mapping = FALSE;
+ mapfn = JS_UNDEFINED;
+ this_arg = JS_UNDEFINED;
+ r = JS_UNDEFINED;
+ arr = JS_UNDEFINED;
+ stack[0] = JS_UNDEFINED;
+ stack[1] = JS_UNDEFINED;
+
+ if (argc > 1) {
+ mapfn = argv[1];
+ if (!JS_IsUndefined(mapfn)) {
+ if (check_function(ctx, mapfn))
+ goto exception;
+ mapping = 1;
+ if (argc > 2)
+ this_arg = argv[2];
+ }
+ }
+ iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(iter))
+ goto exception;
+ if (!JS_IsUndefined(iter)) {
+ JS_FreeValue(ctx, iter);
+ arr = JS_NewArray(ctx);
+ if (JS_IsException(arr))
+ goto exception;
+ stack[0] = JS_DupValue(ctx, items);
+ if (js_for_of_start(ctx, &stack[1], FALSE))
+ goto exception;
+ for (k = 0;; k++) {
+ v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
+ if (JS_IsException(v))
+ goto exception_close;
+ if (done)
+ break;
+ if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception_close;
+ }
+ } else {
+ arr = JS_ToObject(ctx, items);
+ if (JS_IsException(arr))
+ goto exception;
+ }
+ if (js_get_length64(ctx, &len, arr) < 0)
+ goto exception;
+ v = JS_NewInt64(ctx, len);
+ args[0] = v;
+ r = js_typed_array_create(ctx, this_val, 1, args);
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(r))
+ goto exception;
+ for(k = 0; k < len; k++) {
+ v = JS_GetPropertyInt64(ctx, arr, k);
+ if (JS_IsException(v))
+ goto exception;
+ if (mapping) {
+ args[0] = v;
+ args[1] = JS_NewInt32(ctx, k);
+ v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
+ JS_FreeValue(ctx, v);
+ v = v2;
+ if (JS_IsException(v))
+ goto exception;
+ }
+ if (JS_SetPropertyInt64(ctx, r, k, v) < 0)
+ goto exception;
+ }
+ goto done;
+
+ exception_close:
+ if (!JS_IsUndefined(stack[0]))
+ JS_IteratorClose(ctx, stack[0], TRUE);
+ exception:
+ JS_FreeValue(ctx, r);
+ r = JS_EXCEPTION;
+ done:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, stack[0]);
+ JS_FreeValue(ctx, stack[1]);
+ return r;
+}
+
+static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ JSValueConst args[1];
+ int i;
+
+ args[0] = JS_NewInt32(ctx, argc);
+ obj = js_typed_array_create(ctx, this_val, 1, args);
+ if (JS_IsException(obj))
+ return obj;
+
+ for(i = 0; i < argc; i++) {
+ if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ }
+ return obj;
+}
+
+static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ int len, to, from, final, count, shift;
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ return JS_EXCEPTION;
+
+ if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len))
+ return JS_EXCEPTION;
+
+ if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len))
+ return JS_EXCEPTION;
+
+ final = len;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
+ return JS_EXCEPTION;
+ }
+
+ count = min_int(final - from, len - to);
+ if (count > 0) {
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (typed_array_is_detached(ctx, p))
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ shift = typed_array_size_log2(p->class_id);
+ memmove(p->u.array.u.uint8_ptr + (to << shift),
+ p->u.array.u.uint8_ptr + (from << shift),
+ count << shift);
+ }
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ int len, k, final, shift;
+ uint64_t v64;
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_OBJ(this_val);
+
+ if (p->class_id == JS_CLASS_UINT8C_ARRAY) {
+ int32_t v;
+ if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0])))
+ return JS_EXCEPTION;
+ v64 = v;
+ } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) {
+ uint32_t v;
+ if (JS_ToUint32(ctx, &v, argv[0]))
+ return JS_EXCEPTION;
+ v64 = v;
+ } else
+#ifdef CONFIG_BIGNUM
+ if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
+ if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0]))
+ return JS_EXCEPTION;
+ } else
+#endif
+ {
+ double d;
+ if (JS_ToFloat64(ctx, &d, argv[0]))
+ return JS_EXCEPTION;
+ if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
+ union {
+ float f;
+ uint32_t u32;
+ } u;
+ u.f = d;
+ v64 = u.u32;
+ } else {
+ JSFloat64Union u;
+ u.d = d;
+ v64 = u.u64;
+ }
+ }
+
+ k = 0;
+ if (argc > 1) {
+ if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
+ return JS_EXCEPTION;
+ }
+
+ final = len;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
+ return JS_EXCEPTION;
+ }
+
+ if (typed_array_is_detached(ctx, p))
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+
+ shift = typed_array_size_log2(p->class_id);
+ switch(shift) {
+ case 0:
+ if (k < final) {
+ memset(p->u.array.u.uint8_ptr + k, v64, final - k);
+ }
+ break;
+ case 1:
+ for(; k < final; k++) {
+ p->u.array.u.uint16_ptr[k] = v64;
+ }
+ break;
+ case 2:
+ for(; k < final; k++) {
+ p->u.array.u.uint32_ptr[k] = v64;
+ }
+ break;
+ case 3:
+ for(; k < final; k++) {
+ p->u.array.u.uint64_ptr[k] = v64;
+ }
+ break;
+ default:
+ abort();
+ }
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int findIndex)
+{
+ JSValueConst func, this_arg;
+ JSValueConst args[3];
+ JSValue val, index_val, res;
+ int len, k;
+
+ val = JS_UNDEFINED;
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ goto exception;
+
+ func = argv[0];
+ if (check_function(ctx, func))
+ goto exception;
+
+ this_arg = JS_UNDEFINED;
+ if (argc > 1)
+ this_arg = argv[1];
+
+ for(k = 0; k < len; k++) {
+ index_val = JS_NewInt32(ctx, k);
+ val = JS_GetPropertyValue(ctx, this_val, index_val);
+ if (JS_IsException(val))
+ goto exception;
+ args[0] = val;
+ args[1] = index_val;
+ args[2] = this_val;
+ res = JS_Call(ctx, func, this_arg, 3, args);
+ if (JS_IsException(res))
+ goto exception;
+ if (JS_ToBoolFree(ctx, res)) {
+ if (findIndex) {
+ JS_FreeValue(ctx, val);
+ return index_val;
+ } else {
+ return val;
+ }
+ }
+ JS_FreeValue(ctx, val);
+ }
+ if (findIndex)
+ return JS_NewInt32(ctx, -1);
+ else
+ return JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+#define special_indexOf 0
+#define special_lastIndexOf 1
+#define special_includes (-1)
+
+static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int special)
+{
+ JSObject *p;
+ int len, tag, is_int, is_bigint, k, stop, inc, res = -1;
+ int64_t v64;
+ double d;
+ float f;
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ goto exception;
+ if (len == 0)
+ goto done;
+
+ if (special == special_lastIndexOf) {
+ k = len - 1;
+ if (argc > 1) {
+ if (JS_ToFloat64(ctx, &d, argv[1]))
+ goto exception;
+ if (isnan(d)) {
+ k = 0;
+ } else {
+ if (d >= 0) {
+ if (d < k) {
+ k = d;
+ }
+ } else {
+ d += len;
+ if (d < 0)
+ goto done;
+ k = d;
+ }
+ }
+ }
+ stop = -1;
+ inc = -1;
+ } else {
+ k = 0;
+ if (argc > 1) {
+ if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
+ goto exception;
+ }
+ stop = len;
+ inc = 1;
+ }
+
+ p = JS_VALUE_GET_OBJ(this_val);
+ /* if the array was detached, no need to go further (but no
+ exception is raised) */
+ if (typed_array_is_detached(ctx, p)) {
+ /* "includes" scans all the properties, so "undefined" can match */
+ if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0)
+ res = 0;
+ goto done;
+ }
+
+ is_bigint = 0;
+ is_int = 0; /* avoid warning */
+ v64 = 0; /* avoid warning */
+ tag = JS_VALUE_GET_NORM_TAG(argv[0]);
+ if (tag == JS_TAG_INT) {
+ is_int = 1;
+ v64 = JS_VALUE_GET_INT(argv[0]);
+ d = v64;
+ } else if (tag == JS_TAG_FLOAT64) {
+ d = JS_VALUE_GET_FLOAT64(argv[0]);
+ v64 = d;
+ is_int = (v64 == d);
+ } else
+#ifdef CONFIG_BIGNUM
+ if (tag == JS_TAG_BIG_INT) {
+ JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);
+
+ if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
+ if (bf_get_int64(&v64, &p1->num, 0) != 0)
+ goto done;
+ } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
+ if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0)
+ goto done;
+ } else {
+ goto done;
+ }
+ d = 0;
+ is_bigint = 1;
+ } else
+#endif
+ {
+ goto done;
+ }
+
+ switch (p->class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ if (is_int && (int8_t)v64 == v64)
+ goto scan8;
+ break;
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ if (is_int && (uint8_t)v64 == v64) {
+ const uint8_t *pv, *pp;
+ uint16_t v;
+ scan8:
+ pv = p->u.array.u.uint8_ptr;
+ v = v64;
+ if (inc > 0) {
+ pp = memchr(pv + k, v, len - k);
+ if (pp)
+ res = pp - pv;
+ } else {
+ for (; k != stop; k += inc) {
+ if (pv[k] == v) {
+ res = k;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case JS_CLASS_INT16_ARRAY:
+ if (is_int && (int16_t)v64 == v64)
+ goto scan16;
+ break;
+ case JS_CLASS_UINT16_ARRAY:
+ if (is_int && (uint16_t)v64 == v64) {
+ const uint16_t *pv;
+ uint16_t v;
+ scan16:
+ pv = p->u.array.u.uint16_ptr;
+ v = v64;
+ for (; k != stop; k += inc) {
+ if (pv[k] == v) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+ case JS_CLASS_INT32_ARRAY:
+ if (is_int && (int32_t)v64 == v64)
+ goto scan32;
+ break;
+ case JS_CLASS_UINT32_ARRAY:
+ if (is_int && (uint32_t)v64 == v64) {
+ const uint32_t *pv;
+ uint32_t v;
+ scan32:
+ pv = p->u.array.u.uint32_ptr;
+ v = v64;
+ for (; k != stop; k += inc) {
+ if (pv[k] == v) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+ case JS_CLASS_FLOAT32_ARRAY:
+ if (is_bigint)
+ break;
+ if (isnan(d)) {
+ const float *pv = p->u.array.u.float_ptr;
+ /* special case: indexOf returns -1, includes finds NaN */
+ if (special != special_includes)
+ goto done;
+ for (; k != stop; k += inc) {
+ if (isnan(pv[k])) {
+ res = k;
+ break;
+ }
+ }
+ } else if ((f = (float)d) == d) {
+ const float *pv = p->u.array.u.float_ptr;
+ for (; k != stop; k += inc) {
+ if (pv[k] == f) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+ case JS_CLASS_FLOAT64_ARRAY:
+ if (is_bigint)
+ break;
+ if (isnan(d)) {
+ const double *pv = p->u.array.u.double_ptr;
+ /* special case: indexOf returns -1, includes finds NaN */
+ if (special != special_includes)
+ goto done;
+ for (; k != stop; k += inc) {
+ if (isnan(pv[k])) {
+ res = k;
+ break;
+ }
+ }
+ } else {
+ const double *pv = p->u.array.u.double_ptr;
+ for (; k != stop; k += inc) {
+ if (pv[k] == d) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ if (is_bigint || (is_math_mode(ctx) && is_int &&
+ v64 >= -MAX_SAFE_INTEGER &&
+ v64 <= MAX_SAFE_INTEGER)) {
+ goto scan64;
+ }
+ break;
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ if (is_bigint || (is_math_mode(ctx) && is_int &&
+ v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
+ const uint64_t *pv;
+ uint64_t v;
+ scan64:
+ pv = p->u.array.u.uint64_ptr;
+ v = v64;
+ for (; k != stop; k += inc) {
+ if (pv[k] == v) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+#endif
+ }
+
+done:
+ if (special == special_includes)
+ return JS_NewBool(ctx, res >= 0);
+ else
+ return JS_NewInt32(ctx, res);
+
+exception:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int toLocaleString)
+{
+ JSValue sep = JS_UNDEFINED, el;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p = NULL;
+ int i, n;
+ int c;
+
+ n = js_typed_array_get_length_internal(ctx, this_val);
+ if (n < 0)
+ goto exception;
+
+ c = ','; /* default separator */
+ if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
+ sep = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(sep))
+ goto exception;
+ p = JS_VALUE_GET_STRING(sep);
+ if (p->len == 1 && !p->is_wide_char)
+ c = p->u.str8[0];
+ else
+ c = -1;
+ }
+ string_buffer_init(ctx, b, 0);
+
+ /* XXX: optimize with direct access */
+ for(i = 0; i < n; i++) {
+ if (i > 0) {
+ if (c >= 0) {
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ } else {
+ if (string_buffer_concat(b, p, 0, p->len))
+ goto fail;
+ }
+ }
+ el = JS_GetPropertyUint32(ctx, this_val, i);
+ /* Can return undefined for example if the typed array is detached */
+ if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
+ if (JS_IsException(el))
+ goto fail;
+ if (toLocaleString) {
+ el = JS_ToLocaleStringFree(ctx, el);
+ }
+ if (string_buffer_concat_value_free(b, el))
+ goto fail;
+ }
+ }
+ JS_FreeValue(ctx, sep);
+ return string_buffer_end(b);
+
+fail:
+ string_buffer_free(b);
+ JS_FreeValue(ctx, sep);
+exception:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ int len;
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ return JS_EXCEPTION;
+ if (len > 0) {
+ p = JS_VALUE_GET_OBJ(this_val);
+ switch (typed_array_size_log2(p->class_id)) {
+ case 0:
+ {
+ uint8_t *p1 = p->u.array.u.uint8_ptr;
+ uint8_t *p2 = p1 + len - 1;
+ while (p1 < p2) {
+ uint8_t v = *p1;
+ *p1++ = *p2;
+ *p2-- = v;
+ }
+ }
+ break;
+ case 1:
+ {
+ uint16_t *p1 = p->u.array.u.uint16_ptr;
+ uint16_t *p2 = p1 + len - 1;
+ while (p1 < p2) {
+ uint16_t v = *p1;
+ *p1++ = *p2;
+ *p2-- = v;
+ }
+ }
+ break;
+ case 2:
+ {
+ uint32_t *p1 = p->u.array.u.uint32_ptr;
+ uint32_t *p2 = p1 + len - 1;
+ while (p1 < p2) {
+ uint32_t v = *p1;
+ *p1++ = *p2;
+ *p2-- = v;
+ }
+ }
+ break;
+ case 3:
+ {
+ uint64_t *p1 = p->u.array.u.uint64_ptr;
+ uint64_t *p2 = p1 + len - 1;
+ while (p1 < p2) {
+ uint64_t v = *p1;
+ *p1++ = *p2;
+ *p2-- = v;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst args[2];
+ JSValue arr, val;
+ JSObject *p, *p1;
+ int n, len, start, final, count, shift;
+
+ arr = JS_UNDEFINED;
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ goto exception;
+
+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
+ goto exception;
+ final = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
+ goto exception;
+ }
+ count = max_int(final - start, 0);
+
+ p = get_typed_array(ctx, this_val, 0);
+ if (p == NULL)
+ goto exception;
+ shift = typed_array_size_log2(p->class_id);
+
+ args[0] = this_val;
+ args[1] = JS_NewInt32(ctx, count);
+ arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
+ if (JS_IsException(arr))
+ goto exception;
+
+ if (count > 0) {
+ if (validate_typed_array(ctx, this_val)
+ || validate_typed_array(ctx, arr))
+ goto exception;
+
+ p1 = get_typed_array(ctx, arr, 0);
+ if (p1 != NULL && p->class_id == p1->class_id &&
+ typed_array_get_length(ctx, p1) >= count &&
+ typed_array_get_length(ctx, p) >= start + count) {
+ memcpy(p1->u.array.u.uint8_ptr,
+ p->u.array.u.uint8_ptr + (start << shift),
+ count << shift);
+ } else {
+ for (n = 0; n < count; n++) {
+ val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n));
+ if (JS_IsException(val))
+ goto exception;
+ if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val,
+ JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ }
+ return arr;
+
+ exception:
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst args[4];
+ JSValue arr, byteOffset, ta_buffer;
+ JSObject *p;
+ int len, start, final, count, shift, offset;
+
+ p = get_typed_array(ctx, this_val, 0);
+ if (!p)
+ goto exception;
+ len = p->u.array.count;
+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
+ goto exception;
+
+ final = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
+ goto exception;
+ }
+ count = max_int(final - start, 0);
+ byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0);
+ if (JS_IsException(byteOffset))
+ goto exception;
+ shift = typed_array_size_log2(p->class_id);
+ offset = JS_VALUE_GET_INT(byteOffset) + (start << shift);
+ JS_FreeValue(ctx, byteOffset);
+ ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0);
+ if (JS_IsException(ta_buffer))
+ goto exception;
+ args[0] = this_val;
+ args[1] = ta_buffer;
+ args[2] = JS_NewInt32(ctx, offset);
+ args[3] = JS_NewInt32(ctx, count);
+ arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args);
+ JS_FreeValue(ctx, ta_buffer);
+ return arr;
+
+ exception:
+ return JS_EXCEPTION;
+}
+
+/* TypedArray.prototype.sort */
+
+static int js_cmp_doubles(double x, double y)
+{
+ if (isnan(x)) return isnan(y) ? 0 : +1;
+ if (isnan(y)) return -1;
+ if (x < y) return -1;
+ if (x > y) return 1;
+ if (x != 0) return 0;
+ if (signbit(x)) return signbit(y) ? 0 : -1;
+ else return signbit(y) ? 1 : 0;
+}
+
+static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) {
+ return *(const int8_t *)a - *(const int8_t *)b;
+}
+
+static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) {
+ return *(const uint8_t *)a - *(const uint8_t *)b;
+}
+
+static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) {
+ return *(const int16_t *)a - *(const int16_t *)b;
+}
+
+static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) {
+ return *(const uint16_t *)a - *(const uint16_t *)b;
+}
+
+static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) {
+ int32_t x = *(const int32_t *)a;
+ int32_t y = *(const int32_t *)b;
+ return (y < x) - (y > x);
+}
+
+static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) {
+ uint32_t x = *(const uint32_t *)a;
+ uint32_t y = *(const uint32_t *)b;
+ return (y < x) - (y > x);
+}
+
+#ifdef CONFIG_BIGNUM
+static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) {
+ int64_t x = *(const int64_t *)a;
+ int64_t y = *(const int64_t *)b;
+ return (y < x) - (y > x);
+}
+
+static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {
+ uint64_t x = *(const uint64_t *)a;
+ uint64_t y = *(const uint64_t *)b;
+ return (y < x) - (y > x);
+}
+#endif
+
+static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {
+ return js_cmp_doubles(*(const float *)a, *(const float *)b);
+}
+
+static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) {
+ return js_cmp_doubles(*(const double *)a, *(const double *)b);
+}
+
+static JSValue js_TA_get_int8(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const int8_t *)a);
+}
+
+static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const uint8_t *)a);
+}
+
+static JSValue js_TA_get_int16(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const int16_t *)a);
+}
+
+static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const uint16_t *)a);
+}
+
+static JSValue js_TA_get_int32(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const int32_t *)a);
+}
+
+static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) {
+ return JS_NewUint32(ctx, *(const uint32_t *)a);
+}
+
+#ifdef CONFIG_BIGNUM
+static JSValue js_TA_get_int64(JSContext *ctx, const void *a) {
+ return JS_NewBigInt64(ctx, *(int64_t *)a);
+}
+
+static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
+ return JS_NewBigUint64(ctx, *(uint64_t *)a);
+}
+#endif
+
+static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
+ return JS_NewFloat64Impl(ctx, *(const float *)a);
+}
+
+static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
+ return JS_NewFloat64Impl(ctx, *(const double *)a);
+}
+
+struct TA_sort_context {
+ JSContext *ctx;
+ int exception;
+ JSValueConst arr;
+ JSValueConst cmp;
+ JSValue (*getfun)(JSContext *ctx, const void *a);
+ uint8_t *array_ptr; /* cannot change unless the array is detached */
+ int elt_size;
+};
+
+static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
+ struct TA_sort_context *psc = opaque;
+ JSContext *ctx = psc->ctx;
+ uint32_t a_idx, b_idx;
+ JSValueConst argv[2];
+ JSValue res;
+ int cmp;
+
+ cmp = 0;
+ if (!psc->exception) {
+ a_idx = *(uint32_t *)a;
+ b_idx = *(uint32_t *)b;
+ argv[0] = psc->getfun(ctx, psc->array_ptr +
+ a_idx * (size_t)psc->elt_size);
+ argv[1] = psc->getfun(ctx, psc->array_ptr +
+ b_idx * (size_t)(psc->elt_size));
+ res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
+ if (JS_IsException(res)) {
+ psc->exception = 1;
+ goto done;
+ }
+ if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
+ int val = JS_VALUE_GET_INT(res);
+ cmp = (val > 0) - (val < 0);
+ } else {
+ double val;
+ if (JS_ToFloat64Free(ctx, &val, res) < 0) {
+ psc->exception = 1;
+ goto done;
+ } else {
+ cmp = (val > 0) - (val < 0);
+ }
+ }
+ if (cmp == 0) {
+ /* make sort stable: compare array offsets */
+ cmp = (a_idx > b_idx) - (a_idx < b_idx);
+ }
+ if (validate_typed_array(ctx, psc->arr) < 0) {
+ psc->exception = 1;
+ }
+ done:
+ JS_FreeValue(ctx, argv[0]);
+ JS_FreeValue(ctx, argv[1]);
+ }
+ return cmp;
+}
+
+static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ int len;
+ size_t elt_size;
+ struct TA_sort_context tsc;
+ void *array_ptr;
+ int (*cmpfun)(const void *a, const void *b, void *opaque);
+
+ tsc.ctx = ctx;
+ tsc.exception = 0;
+ tsc.arr = this_val;
+ tsc.cmp = argv[0];
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ return JS_EXCEPTION;
+ if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
+ return JS_EXCEPTION;
+
+ if (len > 1) {
+ p = JS_VALUE_GET_OBJ(this_val);
+ switch (p->class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ tsc.getfun = js_TA_get_int8;
+ cmpfun = js_TA_cmp_int8;
+ break;
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ tsc.getfun = js_TA_get_uint8;
+ cmpfun = js_TA_cmp_uint8;
+ break;
+ case JS_CLASS_INT16_ARRAY:
+ tsc.getfun = js_TA_get_int16;
+ cmpfun = js_TA_cmp_int16;
+ break;
+ case JS_CLASS_UINT16_ARRAY:
+ tsc.getfun = js_TA_get_uint16;
+ cmpfun = js_TA_cmp_uint16;
+ break;
+ case JS_CLASS_INT32_ARRAY:
+ tsc.getfun = js_TA_get_int32;
+ cmpfun = js_TA_cmp_int32;
+ break;
+ case JS_CLASS_UINT32_ARRAY:
+ tsc.getfun = js_TA_get_uint32;
+ cmpfun = js_TA_cmp_uint32;
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ tsc.getfun = js_TA_get_int64;
+ cmpfun = js_TA_cmp_int64;
+ break;
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ tsc.getfun = js_TA_get_uint64;
+ cmpfun = js_TA_cmp_uint64;
+ break;
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ tsc.getfun = js_TA_get_float32;
+ cmpfun = js_TA_cmp_float32;
+ break;
+ case JS_CLASS_FLOAT64_ARRAY:
+ tsc.getfun = js_TA_get_float64;
+ cmpfun = js_TA_cmp_float64;
+ break;
+ default:
+ abort();
+ }
+ array_ptr = p->u.array.u.ptr;
+ elt_size = 1 << typed_array_size_log2(p->class_id);
+ if (!JS_IsUndefined(tsc.cmp)) {
+ uint32_t *array_idx;
+ void *array_tmp;
+ size_t i, j;
+
+ /* XXX: a stable sort would use less memory */
+ array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
+ if (!array_idx)
+ return JS_EXCEPTION;
+ for(i = 0; i < len; i++)
+ array_idx[i] = i;
+ tsc.array_ptr = array_ptr;
+ tsc.elt_size = elt_size;
+ rqsort(array_idx, len, sizeof(array_idx[0]),
+ js_TA_cmp_generic, &tsc);
+ if (tsc.exception)
+ goto fail;
+ array_tmp = js_malloc(ctx, len * elt_size);
+ if (!array_tmp) {
+ fail:
+ js_free(ctx, array_idx);
+ return JS_EXCEPTION;
+ }
+ memcpy(array_tmp, array_ptr, len * elt_size);
+ switch(elt_size) {
+ case 1:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];
+ }
+ break;
+ case 2:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];
+ }
+ break;
+ case 4:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];
+ }
+ break;
+ case 8:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];
+ }
+ break;
+ default:
+ abort();
+ }
+ js_free(ctx, array_tmp);
+ js_free(ctx, array_idx);
+ } else {
+ rqsort(array_ptr, len, elt_size, cmpfun, &tsc);
+ if (tsc.exception)
+ return JS_EXCEPTION;
+ }
+ }
+ return JS_DupValue(ctx, this_val);
+}
+
+static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
+ JS_CFUNC_DEF("from", 1, js_typed_array_from ),
+ JS_CFUNC_DEF("of", 0, js_typed_array_of ),
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+ //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ),
+ //JS_CFUNC_DEF("__create", 2, js_typed_array___create ),
+ //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ),
+};
+
+static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
+ JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),
+ JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ),
+ JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ),
+ JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ),
+ JS_CFUNC_DEF("set", 1, js_typed_array_set ),
+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ),
+ JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
+ JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ),
+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
+ JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ),
+ JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ),
+ JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ),
+ JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ),
+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ),
+ JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ),
+ JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ),
+ JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ),
+ JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ),
+ JS_CFUNC_DEF("fill", 1, js_typed_array_fill ),
+ JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0 ),
+ JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1 ),
+ JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),
+ JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),
+ JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
+ JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),
+ JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),
+ JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),
+ JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ),
+ JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ),
+ //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@
+};
+
+static JSValue js_typed_array_base_constructor(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ThrowTypeError(ctx, "cannot be called");
+}
+
+/* 'obj' must be an allocated typed array object */
+static int typed_array_init(JSContext *ctx, JSValueConst obj,
+ JSValue buffer, uint64_t offset, uint64_t len)
+{
+ JSTypedArray *ta;
+ JSObject *p, *pbuffer;
+ JSArrayBuffer *abuf;
+ int size_log2;
+
+ p = JS_VALUE_GET_OBJ(obj);
+ size_log2 = typed_array_size_log2(p->class_id);
+ ta = js_malloc(ctx, sizeof(*ta));
+ if (!ta) {
+ JS_FreeValue(ctx, buffer);
+ return -1;
+ }
+ pbuffer = JS_VALUE_GET_OBJ(buffer);
+ abuf = pbuffer->u.array_buffer;
+ ta->obj = p;
+ ta->buffer = pbuffer;
+ ta->offset = offset;
+ ta->length = len << size_log2;
+ list_add_tail(&ta->link, &abuf->array_list);
+ p->u.typed_array = ta;
+ p->u.array.count = len;
+ p->u.array.u.ptr = abuf->data + offset;
+ return 0;
+}
+
+
+static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
+ JSValueConst obj, JSValueConst method)
+{
+ JSValue arr, iter, next_method = JS_UNDEFINED, val;
+ BOOL done;
+ uint32_t k;
+
+ *plen = 0;
+ arr = JS_NewArray(ctx);
+ if (JS_IsException(arr))
+ return arr;
+ iter = JS_GetIterator2(ctx, obj, method);
+ if (JS_IsException(iter))
+ goto fail;
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail;
+ k = 0;
+ for(;;) {
+ val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(val))
+ goto fail;
+ if (done) {
+ JS_FreeValue(ctx, val);
+ break;
+ }
+ if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0)
+ goto fail;
+ k++;
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ *plen = k;
+ return arr;
+ fail:
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_constructor_obj(JSContext *ctx,
+ JSValueConst new_target,
+ JSValueConst obj,
+ int classid)
+{
+ JSValue iter, ret, arr = JS_UNDEFINED, val, buffer;
+ uint32_t i;
+ int size_log2;
+ int64_t len;
+
+ size_log2 = typed_array_size_log2(classid);
+ ret = js_create_from_ctor(ctx, new_target, classid);
+ if (JS_IsException(ret))
+ return JS_EXCEPTION;
+
+ iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(iter))
+ goto fail;
+ if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
+ uint32_t len1;
+ arr = js_array_from_iterator(ctx, &len1, obj, iter);
+ JS_FreeValue(ctx, iter);
+ if (JS_IsException(arr))
+ goto fail;
+ len = len1;
+ } else {
+ if (js_get_length64(ctx, &len, obj))
+ goto fail;
+ arr = JS_DupValue(ctx, obj);
+ }
+
+ buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
+ len << size_log2);
+ if (JS_IsException(buffer))
+ goto fail;
+ if (typed_array_init(ctx, ret, buffer, 0, len))
+ goto fail;
+
+ for(i = 0; i < len; i++) {
+ val = JS_GetPropertyUint32(ctx, arr, i);
+ if (JS_IsException(val))
+ goto fail;
+ if (JS_SetPropertyUint32(ctx, ret, i, val) < 0)
+ goto fail;
+ }
+ JS_FreeValue(ctx, arr);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_constructor_ta(JSContext *ctx,
+ JSValueConst new_target,
+ JSValueConst src_obj,
+ int classid)
+{
+ JSObject *p, *src_buffer;
+ JSTypedArray *ta;
+ JSValue ctor, obj, buffer;
+ uint32_t len, i;
+ int size_log2;
+ JSArrayBuffer *src_abuf, *abuf;
+
+ obj = js_create_from_ctor(ctx, new_target, classid);
+ if (JS_IsException(obj))
+ return obj;
+ p = JS_VALUE_GET_OBJ(src_obj);
+ if (typed_array_is_detached(ctx, p)) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ ta = p->u.typed_array;
+ len = p->u.array.count;
+ src_buffer = ta->buffer;
+ src_abuf = src_buffer->u.array_buffer;
+ if (!src_abuf->shared) {
+ ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer),
+ JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ goto fail;
+ } else {
+ /* force ArrayBuffer default constructor */
+ ctor = JS_UNDEFINED;
+ }
+ size_log2 = typed_array_size_log2(classid);
+ buffer = js_array_buffer_constructor1(ctx, ctor,
+ (uint64_t)len << size_log2);
+ JS_FreeValue(ctx, ctor);
+ if (JS_IsException(buffer))
+ goto fail;
+ /* necessary because it could have been detached */
+ if (typed_array_is_detached(ctx, p)) {
+ JS_FreeValue(ctx, buffer);
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);
+ if (typed_array_init(ctx, obj, buffer, 0, len))
+ goto fail;
+ if (p->class_id == classid) {
+ /* same type: copy the content */
+ memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);
+ } else {
+ for(i = 0; i < len; i++) {
+ JSValue val;
+ val = JS_GetPropertyUint32(ctx, src_obj, i);
+ if (JS_IsException(val))
+ goto fail;
+ if (JS_SetPropertyUint32(ctx, obj, i, val) < 0)
+ goto fail;
+ }
+ }
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv,
+ int classid)
+{
+ JSValue buffer, obj;
+ JSArrayBuffer *abuf;
+ int size_log2;
+ uint64_t len, offset;
+
+ size_log2 = typed_array_size_log2(classid);
+ if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {
+ if (JS_ToIndex(ctx, &len, argv[0]))
+ return JS_EXCEPTION;
+ buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
+ len << size_log2);
+ if (JS_IsException(buffer))
+ return JS_EXCEPTION;
+ offset = 0;
+ } else {
+ JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
+ if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
+ p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
+ abuf = p->u.array_buffer;
+ if (JS_ToIndex(ctx, &offset, argv[1]))
+ return JS_EXCEPTION;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if ((offset & ((1 << size_log2) - 1)) != 0 ||
+ offset > abuf->byte_length)
+ return JS_ThrowRangeError(ctx, "invalid offset");
+ if (JS_IsUndefined(argv[2])) {
+ if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)
+ goto invalid_length;
+ len = (abuf->byte_length - offset) >> size_log2;
+ } else {
+ if (JS_ToIndex(ctx, &len, argv[2]))
+ return JS_EXCEPTION;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if ((offset + (len << size_log2)) > abuf->byte_length) {
+ invalid_length:
+ return JS_ThrowRangeError(ctx, "invalid length");
+ }
+ }
+ buffer = JS_DupValue(ctx, argv[0]);
+ } else {
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid);
+ } else {
+ return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid);
+ }
+ }
+ }
+
+ obj = js_create_from_ctor(ctx, new_target, classid);
+ if (JS_IsException(obj)) {
+ JS_FreeValue(ctx, buffer);
+ return JS_EXCEPTION;
+ }
+ if (typed_array_init(ctx, obj, buffer, offset, len)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSTypedArray *ta = p->u.typed_array;
+ if (ta) {
+ /* during the GC the finalizers are called in an arbitrary
+ order so the ArrayBuffer finalizer may have been called */
+ if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) {
+ list_del(&ta->link);
+ }
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
+ js_free_rt(rt, ta);
+ }
+}
+
+static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSTypedArray *ta = p->u.typed_array;
+ if (ta) {
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func);
+ }
+}
+
+static JSValue js_dataview_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSArrayBuffer *abuf;
+ uint64_t offset;
+ uint32_t len;
+ JSValueConst buffer;
+ JSValue obj;
+ JSTypedArray *ta;
+ JSObject *p;
+
+ buffer = argv[0];
+ abuf = js_get_array_buffer(ctx, buffer);
+ if (!abuf)
+ return JS_EXCEPTION;
+ offset = 0;
+ if (argc > 1) {
+ if (JS_ToIndex(ctx, &offset, argv[1]))
+ return JS_EXCEPTION;
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if (offset > abuf->byte_length)
+ return JS_ThrowRangeError(ctx, "invalid byteOffset");
+ len = abuf->byte_length - offset;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ uint64_t l;
+ if (JS_ToIndex(ctx, &l, argv[2]))
+ return JS_EXCEPTION;
+ if (l > len)
+ return JS_ThrowRangeError(ctx, "invalid byteLength");
+ len = l;
+ }
+
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ if (abuf->detached) {
+ /* could have been detached in js_create_from_ctor() */
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ ta = js_malloc(ctx, sizeof(*ta));
+ if (!ta) {
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ p = JS_VALUE_GET_OBJ(obj);
+ ta->obj = p;
+ ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer));
+ ta->offset = offset;
+ ta->length = len;
+ list_add_tail(&ta->link, &abuf->array_list);
+ p->u.typed_array = ta;
+ return obj;
+}
+
+static JSValue js_dataview_getValue(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int class_id)
+{
+ JSTypedArray *ta;
+ JSArrayBuffer *abuf;
+ int is_swap, size;
+ uint8_t *ptr;
+ uint32_t v;
+ uint64_t pos;
+
+ ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
+ if (!ta)
+ return JS_EXCEPTION;
+ size = 1 << typed_array_size_log2(class_id);
+ if (JS_ToIndex(ctx, &pos, argv[0]))
+ return JS_EXCEPTION;
+ is_swap = FALSE;
+ if (argc > 1)
+ is_swap = JS_ToBool(ctx, argv[1]);
+#ifndef WORDS_BIGENDIAN
+ is_swap ^= 1;
+#endif
+ abuf = ta->buffer->u.array_buffer;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if ((pos + size) > ta->length)
+ return JS_ThrowRangeError(ctx, "out of bound");
+ ptr = abuf->data + ta->offset + pos;
+
+ switch(class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ return JS_NewInt32(ctx, *(int8_t *)ptr);
+ case JS_CLASS_UINT8_ARRAY:
+ return JS_NewInt32(ctx, *ptr);
+ case JS_CLASS_INT16_ARRAY:
+ v = get_u16(ptr);
+ if (is_swap)
+ v = bswap16(v);
+ return JS_NewInt32(ctx, (int16_t)v);
+ case JS_CLASS_UINT16_ARRAY:
+ v = get_u16(ptr);
+ if (is_swap)
+ v = bswap16(v);
+ return JS_NewInt32(ctx, v);
+ case JS_CLASS_INT32_ARRAY:
+ v = get_u32(ptr);
+ if (is_swap)
+ v = bswap32(v);
+ return JS_NewInt32(ctx, v);
+ case JS_CLASS_UINT32_ARRAY:
+ v = get_u32(ptr);
+ if (is_swap)
+ v = bswap32(v);
+ return JS_NewUint32(ctx, v);
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ {
+ uint64_t v;
+ v = get_u64(ptr);
+ if (is_swap)
+ v = bswap64(v);
+ return JS_NewBigInt64(ctx, v);
+ }
+ break;
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ {
+ uint64_t v;
+ v = get_u64(ptr);
+ if (is_swap)
+ v = bswap64(v);
+ return JS_NewBigUint64(ctx, v);
+ }
+ break;
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ {
+ union {
+ float f;
+ uint32_t i;
+ } u;
+ v = get_u32(ptr);
+ if (is_swap)
+ v = bswap32(v);
+ u.i = v;
+ return JS_NewFloat64Impl(ctx, u.f);
+ }
+ case JS_CLASS_FLOAT64_ARRAY:
+ {
+ union {
+ double f;
+ uint64_t i;
+ } u;
+ u.i = get_u64(ptr);
+ if (is_swap)
+ u.i = bswap64(u.i);
+ return JS_NewFloat64Impl(ctx, u.f);
+ }
+ default:
+ abort();
+ }
+}
+
+static JSValue js_dataview_setValue(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int class_id)
+{
+ JSTypedArray *ta;
+ JSArrayBuffer *abuf;
+ int is_swap, size;
+ uint8_t *ptr;
+ uint64_t v64;
+ uint32_t v;
+ uint64_t pos;
+ JSValueConst val;
+
+ ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
+ if (!ta)
+ return JS_EXCEPTION;
+ size = 1 << typed_array_size_log2(class_id);
+ if (JS_ToIndex(ctx, &pos, argv[0]))
+ return JS_EXCEPTION;
+ val = argv[1];
+ v = 0; /* avoid warning */
+ v64 = 0; /* avoid warning */
+ if (class_id <= JS_CLASS_UINT32_ARRAY) {
+ if (JS_ToUint32(ctx, &v, val))
+ return JS_EXCEPTION;
+ } else
+#ifdef CONFIG_BIGNUM
+ if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
+ if (JS_ToBigInt64(ctx, (int64_t *)&v64, val))
+ return JS_EXCEPTION;
+ } else
+#endif
+ {
+ double d;
+ if (JS_ToFloat64(ctx, &d, val))
+ return JS_EXCEPTION;
+ if (class_id == JS_CLASS_FLOAT32_ARRAY) {
+ union {
+ float f;
+ uint32_t i;
+ } u;
+ u.f = d;
+ v = u.i;
+ } else {
+ JSFloat64Union u;
+ u.d = d;
+ v64 = u.u64;
+ }
+ }
+ is_swap = FALSE;
+ if (argc > 2)
+ is_swap = JS_ToBool(ctx, argv[2]);
+#ifndef WORDS_BIGENDIAN
+ is_swap ^= 1;
+#endif
+ abuf = ta->buffer->u.array_buffer;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if ((pos + size) > ta->length)
+ return JS_ThrowRangeError(ctx, "out of bound");
+ ptr = abuf->data + ta->offset + pos;
+
+ switch(class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ *ptr = v;
+ break;
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ if (is_swap)
+ v = bswap16(v);
+ put_u16(ptr, v);
+ break;
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+ case JS_CLASS_FLOAT32_ARRAY:
+ if (is_swap)
+ v = bswap32(v);
+ put_u32(ptr, v);
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+#endif
+ case JS_CLASS_FLOAT64_ARRAY:
+ if (is_swap)
+ v64 = bswap64(v64);
+ put_u64(ptr, v64);
+ break;
+ default:
+ abort();
+ }
+ return JS_UNDEFINED;
+}
+
+static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ),
+ JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ),
+ JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ),
+ JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),
+#ifdef CONFIG_BIGNUM
+ JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),
+#endif
+ JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),
+#ifdef CONFIG_BIGNUM
+ JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),
+#endif
+ JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),
+};
+
+/* Atomics */
+#ifdef CONFIG_ATOMICS
+
+typedef enum AtomicsOpEnum {
+ ATOMICS_OP_ADD,
+ ATOMICS_OP_AND,
+ ATOMICS_OP_OR,
+ ATOMICS_OP_SUB,
+ ATOMICS_OP_XOR,
+ ATOMICS_OP_EXCHANGE,
+ ATOMICS_OP_COMPARE_EXCHANGE,
+ ATOMICS_OP_LOAD,
+} AtomicsOpEnum;
+
+static void *js_atomics_get_ptr(JSContext *ctx,
+ JSArrayBuffer **pabuf,
+ int *psize_log2, JSClassID *pclass_id,
+ JSValueConst obj, JSValueConst idx_val,
+ int is_waitable)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ JSArrayBuffer *abuf;
+ void *ptr;
+ uint64_t idx;
+ BOOL err;
+ int size_log2;
+
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ goto fail;
+ p = JS_VALUE_GET_OBJ(obj);
+#ifdef CONFIG_BIGNUM
+ if (is_waitable)
+ err = (p->class_id != JS_CLASS_INT32_ARRAY &&
+ p->class_id != JS_CLASS_BIG_INT64_ARRAY);
+ else
+ err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
+ p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);
+#else
+ if (is_waitable)
+ err = (p->class_id != JS_CLASS_INT32_ARRAY);
+ else
+ err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
+ p->class_id <= JS_CLASS_UINT32_ARRAY);
+#endif
+ if (err) {
+ fail:
+ JS_ThrowTypeError(ctx, "integer TypedArray expected");
+ return NULL;
+ }
+ ta = p->u.typed_array;
+ abuf = ta->buffer->u.array_buffer;
+ if (!abuf->shared) {
+ if (is_waitable == 2) {
+ JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
+ return NULL;
+ }
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ return NULL;
+ }
+ }
+ if (JS_ToIndex(ctx, &idx, idx_val)) {
+ return NULL;
+ }
+ /* if the array buffer is detached, p->u.array.count = 0 */
+ if (idx >= p->u.array.count) {
+ JS_ThrowRangeError(ctx, "out-of-bound access");
+ return NULL;
+ }
+ size_log2 = typed_array_size_log2(p->class_id);
+ ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+ if (pabuf)
+ *pabuf = abuf;
+ if (psize_log2)
+ *psize_log2 = size_log2;
+ if (pclass_id)
+ *pclass_id = p->class_id;
+ return ptr;
+}
+
+static JSValue js_atomics_op(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int op)
+{
+ int size_log2;
+#ifdef CONFIG_BIGNUM
+ uint64_t v, a, rep_val;
+#else
+ uint32_t v, a, rep_val;
+#endif
+ void *ptr;
+ JSValue ret;
+ JSClassID class_id;
+ JSArrayBuffer *abuf;
+
+ ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
+ argv[0], argv[1], 0);
+ if (!ptr)
+ return JS_EXCEPTION;
+ rep_val = 0;
+ if (op == ATOMICS_OP_LOAD) {
+ v = 0;
+ } else {
+#ifdef CONFIG_BIGNUM
+ if (size_log2 == 3) {
+ int64_t v64;
+ if (JS_ToBigInt64(ctx, &v64, argv[2]))
+ return JS_EXCEPTION;
+ v = v64;
+ if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
+ if (JS_ToBigInt64(ctx, &v64, argv[3]))
+ return JS_EXCEPTION;
+ rep_val = v64;
+ }
+ } else
+#endif
+ {
+ uint32_t v32;
+ if (JS_ToUint32(ctx, &v32, argv[2]))
+ return JS_EXCEPTION;
+ v = v32;
+ if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
+ if (JS_ToUint32(ctx, &v32, argv[3]))
+ return JS_EXCEPTION;
+ rep_val = v32;
+ }
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ }
+
+ switch(op | (size_log2 << 3)) {
+
+#ifdef CONFIG_BIGNUM
+#define OP(op_name, func_name) \
+ case ATOMICS_OP_ ## op_name | (0 << 3): \
+ a = func_name((_Atomic(uint8_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (1 << 3): \
+ a = func_name((_Atomic(uint16_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (2 << 3): \
+ a = func_name((_Atomic(uint32_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (3 << 3): \
+ a = func_name((_Atomic(uint64_t) *)ptr, v); \
+ break;
+#else
+#define OP(op_name, func_name) \
+ case ATOMICS_OP_ ## op_name | (0 << 3): \
+ a = func_name((_Atomic(uint8_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (1 << 3): \
+ a = func_name((_Atomic(uint16_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (2 << 3): \
+ a = func_name((_Atomic(uint32_t) *)ptr, v); \
+ break;
+#endif
+ OP(ADD, atomic_fetch_add)
+ OP(AND, atomic_fetch_and)
+ OP(OR, atomic_fetch_or)
+ OP(SUB, atomic_fetch_sub)
+ OP(XOR, atomic_fetch_xor)
+ OP(EXCHANGE, atomic_exchange)
+#undef OP
+
+ case ATOMICS_OP_LOAD | (0 << 3):
+ a = atomic_load((_Atomic(uint8_t) *)ptr);
+ break;
+ case ATOMICS_OP_LOAD | (1 << 3):
+ a = atomic_load((_Atomic(uint16_t) *)ptr);
+ break;
+ case ATOMICS_OP_LOAD | (2 << 3):
+ a = atomic_load((_Atomic(uint32_t) *)ptr);
+ break;
+#ifdef CONFIG_BIGNUM
+ case ATOMICS_OP_LOAD | (3 << 3):
+ a = atomic_load((_Atomic(uint64_t) *)ptr);
+ break;
+#endif
+
+ case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):
+ {
+ uint8_t v1 = v;
+ atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);
+ a = v1;
+ }
+ break;
+ case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):
+ {
+ uint16_t v1 = v;
+ atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);
+ a = v1;
+ }
+ break;
+ case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):
+ {
+ uint32_t v1 = v;
+ atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);
+ a = v1;
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):
+ {
+ uint64_t v1 = v;
+ atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);
+ a = v1;
+ }
+ break;
+#endif
+ default:
+ abort();
+ }
+
+ switch(class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ a = (int8_t)a;
+ goto done;
+ case JS_CLASS_UINT8_ARRAY:
+ a = (uint8_t)a;
+ goto done;
+ case JS_CLASS_INT16_ARRAY:
+ a = (int16_t)a;
+ goto done;
+ case JS_CLASS_UINT16_ARRAY:
+ a = (uint16_t)a;
+ goto done;
+ case JS_CLASS_INT32_ARRAY:
+ done:
+ ret = JS_NewInt32(ctx, a);
+ break;
+ case JS_CLASS_UINT32_ARRAY:
+ ret = JS_NewUint32(ctx, a);
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ ret = JS_NewBigInt64(ctx, a);
+ break;
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ ret = JS_NewBigUint64(ctx, a);
+ break;
+#endif
+ default:
+ abort();
+ }
+ return ret;
+}
+
+static JSValue js_atomics_store(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ int size_log2;
+ void *ptr;
+ JSValue ret;
+ JSArrayBuffer *abuf;
+
+ ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
+ argv[0], argv[1], 0);
+ if (!ptr)
+ return JS_EXCEPTION;
+#ifdef CONFIG_BIGNUM
+ if (size_log2 == 3) {
+ int64_t v64;
+ ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));
+ if (JS_IsException(ret))
+ return ret;
+ if (JS_ToBigInt64(ctx, &v64, ret)) {
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ atomic_store((_Atomic(uint64_t) *)ptr, v64);
+ } else
+#endif
+ {
+ uint32_t v;
+ /* XXX: spec, would be simpler to return the written value */
+ ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
+ if (JS_IsException(ret))
+ return ret;
+ if (JS_ToUint32(ctx, &v, ret)) {
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ switch(size_log2) {
+ case 0:
+ atomic_store((_Atomic(uint8_t) *)ptr, v);
+ break;
+ case 1:
+ atomic_store((_Atomic(uint16_t) *)ptr, v);
+ break;
+ case 2:
+ atomic_store((_Atomic(uint32_t) *)ptr, v);
+ break;
+ default:
+ abort();
+ }
+ }
+ return ret;
+}
+
+static JSValue js_atomics_isLockFree(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ int v, ret;
+ if (JS_ToInt32Sat(ctx, &v, argv[0]))
+ return JS_EXCEPTION;
+ ret = (v == 1 || v == 2 || v == 4
+#ifdef CONFIG_BIGNUM
+ || v == 8
+#endif
+ );
+ return JS_NewBool(ctx, ret);
+}
+
+typedef struct JSAtomicsWaiter {
+ struct list_head link;
+ BOOL linked;
+ pthread_cond_t cond;
+ int32_t *ptr;
+} JSAtomicsWaiter;
+
+static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct list_head js_atomics_waiter_list =
+ LIST_HEAD_INIT(js_atomics_waiter_list);
+
+static JSValue js_atomics_wait(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ int64_t v;
+ int32_t v32;
+ void *ptr;
+ int64_t timeout;
+ struct timespec ts;
+ JSAtomicsWaiter waiter_s, *waiter;
+ int ret, size_log2, res;
+ double d;
+
+ ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
+ argv[0], argv[1], 2);
+ if (!ptr)
+ return JS_EXCEPTION;
+#ifdef CONFIG_BIGNUM
+ if (size_log2 == 3) {
+ if (JS_ToBigInt64(ctx, &v, argv[2]))
+ return JS_EXCEPTION;
+ } else
+#endif
+ {
+ if (JS_ToInt32(ctx, &v32, argv[2]))
+ return JS_EXCEPTION;
+ v = v32;
+ }
+ if (JS_ToFloat64(ctx, &d, argv[3]))
+ return JS_EXCEPTION;
+ if (isnan(d) || d > INT64_MAX)
+ timeout = INT64_MAX;
+ else if (d < 0)
+ timeout = 0;
+ else
+ timeout = (int64_t)d;
+ if (!ctx->rt->can_block)
+ return JS_ThrowTypeError(ctx, "cannot block in this thread");
+
+ /* XXX: inefficient if large number of waiters, should hash on
+ 'ptr' value */
+ /* XXX: use Linux futexes when available ? */
+ pthread_mutex_lock(&js_atomics_mutex);
+ if (size_log2 == 3) {
+ res = *(int64_t *)ptr != v;
+ } else {
+ res = *(int32_t *)ptr != v;
+ }
+ if (res) {
+ pthread_mutex_unlock(&js_atomics_mutex);
+ return JS_AtomToString(ctx, JS_ATOM_not_equal);
+ }
+
+ waiter = &waiter_s;
+ waiter->ptr = ptr;
+ pthread_cond_init(&waiter->cond, NULL);
+ waiter->linked = TRUE;
+ list_add_tail(&waiter->link, &js_atomics_waiter_list);
+
+ if (timeout == INT64_MAX) {
+ pthread_cond_wait(&waiter->cond, &js_atomics_mutex);
+ ret = 0;
+ } else {
+ /* XXX: use clock monotonic */
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += timeout / 1000;
+ ts.tv_nsec += (timeout % 1000) * 1000000;
+ if (ts.tv_nsec >= 1000000000) {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+ ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,
+ &ts);
+ }
+ if (waiter->linked)
+ list_del(&waiter->link);
+ pthread_mutex_unlock(&js_atomics_mutex);
+ pthread_cond_destroy(&waiter->cond);
+ if (ret == ETIMEDOUT) {
+ return JS_AtomToString(ctx, JS_ATOM_timed_out);
+ } else {
+ return JS_AtomToString(ctx, JS_ATOM_ok);
+ }
+}
+
+static JSValue js_atomics_notify(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ struct list_head *el, *el1, waiter_list;
+ int32_t count, n;
+ void *ptr;
+ JSAtomicsWaiter *waiter;
+ JSArrayBuffer *abuf;
+
+ ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
+ if (!ptr)
+ return JS_EXCEPTION;
+
+ if (JS_IsUndefined(argv[2])) {
+ count = INT32_MAX;
+ } else {
+ if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
+ return JS_EXCEPTION;
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+
+ n = 0;
+ if (abuf->shared && count > 0) {
+ pthread_mutex_lock(&js_atomics_mutex);
+ init_list_head(&waiter_list);
+ list_for_each_safe(el, el1, &js_atomics_waiter_list) {
+ waiter = list_entry(el, JSAtomicsWaiter, link);
+ if (waiter->ptr == ptr) {
+ list_del(&waiter->link);
+ waiter->linked = FALSE;
+ list_add_tail(&waiter->link, &waiter_list);
+ n++;
+ if (n >= count)
+ break;
+ }
+ }
+ list_for_each(el, &waiter_list) {
+ waiter = list_entry(el, JSAtomicsWaiter, link);
+ pthread_cond_signal(&waiter->cond);
+ }
+ pthread_mutex_unlock(&js_atomics_mutex);
+ }
+ return JS_NewInt32(ctx, n);
+}
+
+static const JSCFunctionListEntry js_atomics_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
+ JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
+ JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
+ JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
+ JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
+ JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
+ JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
+ JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
+ JS_CFUNC_DEF("store", 3, js_atomics_store ),
+ JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
+ JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
+ JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_atomics_obj[] = {
+ JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+void JS_AddIntrinsicAtomics(JSContext *ctx)
+{
+ /* add Atomics as autoinit object */
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
+}
+
+#endif /* CONFIG_ATOMICS */
+
+void JS_AddIntrinsicTypedArrays(JSContext *ctx)
+{
+ JSValue typed_array_base_proto, typed_array_base_func;
+ JSValueConst array_buffer_func, shared_array_buffer_func;
+ int i;
+
+ ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER],
+ js_array_buffer_proto_funcs,
+ countof(js_array_buffer_proto_funcs));
+
+ array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer",
+ js_array_buffer_constructor, 1,
+ ctx->class_proto[JS_CLASS_ARRAY_BUFFER]);
+ JS_SetPropertyFunctionList(ctx, array_buffer_func,
+ js_array_buffer_funcs,
+ countof(js_array_buffer_funcs));
+
+ ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER],
+ js_shared_array_buffer_proto_funcs,
+ countof(js_shared_array_buffer_proto_funcs));
+
+ shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer",
+ js_shared_array_buffer_constructor, 1,
+ ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]);
+ JS_SetPropertyFunctionList(ctx, shared_array_buffer_func,
+ js_shared_array_buffer_funcs,
+ countof(js_shared_array_buffer_funcs));
+
+ typed_array_base_proto = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, typed_array_base_proto,
+ js_typed_array_base_proto_funcs,
+ countof(js_typed_array_base_proto_funcs));
+
+ /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */
+ JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);
+ /* XXX: should use alias method in JSCFunctionListEntry */ //@@@
+ JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor,
+ "TypedArray", 0);
+ JS_SetPropertyFunctionList(ctx, typed_array_base_func,
+ js_typed_array_base_funcs,
+ countof(js_typed_array_base_funcs));
+ JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto);
+
+ for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
+ JSValue func_obj;
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ const char *name;
+
+ ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto);
+ JS_DefinePropertyValueStr(ctx, ctx->class_proto[i],
+ "BYTES_PER_ELEMENT",
+ JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
+ 0);
+ name = JS_AtomGetStr(ctx, buf, sizeof(buf),
+ JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
+ func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_typed_array_constructor,
+ name, 3, JS_CFUNC_constructor_magic, i,
+ typed_array_base_func);
+ JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]);
+ JS_DefinePropertyValueStr(ctx, func_obj,
+ "BYTES_PER_ELEMENT",
+ JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
+ 0);
+ }
+ JS_FreeValue(ctx, typed_array_base_proto);
+ JS_FreeValue(ctx, typed_array_base_func);
+
+ /* DataView */
+ ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW],
+ js_dataview_proto_funcs,
+ countof(js_dataview_proto_funcs));
+ JS_NewGlobalCConstructorOnly(ctx, "DataView",
+ js_dataview_constructor, 1,
+ ctx->class_proto[JS_CLASS_DATAVIEW]);
+ /* Atomics */
+#ifdef CONFIG_ATOMICS
+ JS_AddIntrinsicAtomics(ctx);
+#endif
+}
+
+#ifndef NDEBUG
+static void *watchedRefCount = NULL;
+
+void notifyRefCountIncrease(JSRefCountHeader *p)
+{
+ if (p == watchedRefCount)
+ fprintf(stderr, "increasing ref count %d for %p\n", p->ref_count, watchedRefCount);
+}
+
+void notifyRefCountDecrease(JSRefCountHeader *p)
+{
+ if (p == watchedRefCount)
+ fprintf(stderr, "decreasing ref count %d for %p\n", p->ref_count, watchedRefCount);
+}
+
+void watchRefCount(void *p)
+{
+ watchedRefCount = p;
+}
+#endif
+
+void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup)
+{
+ ctx->scopeLookup = scopeLookup;
+}
+
+void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler)
+{
+ ctx->handleUndefined = handler;
+}
+
+void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler)
+{
+ ctx->handleFunctionEntered = handler;
+}
+
+void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler)
+{
+ ctx->handleFunctionExited = handler;
+}
+
+int isSimpleValue(JSValue v)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
+ return 1;
+ p = JS_VALUE_GET_OBJ(v);
+ return p->class_id >= JS_CLASS_OBJECT && p->class_id <= JS_CLASS_BOOLEAN;
+}
+
+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);
+}
+
+JSValue mkVal(int32_t tag, int32_t val)
+{
+ return (JSValue){ (JSValueUnion){ .int32 = val }, tag };
+}
+
+JSValue mkPtr(int32_t tag, void *p)
+{
+ return (JSValue){ (JSValueUnion){ .ptr = p }, tag };
+}
+
+void JS_FreeValue(JSContext *ctx, JSValue v) {
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+#ifndef NDEBUG
+ notifyRefCountDecrease(p);
+#endif
+ if (--p->ref_count <= 0) {
+ JS_FreeValueImpl(ctx, v);
+ }
+ }
+}
+void JS_FreeValueRT(JSRuntime *rt, JSValue v) {
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+#ifndef NDEBUG
+ notifyRefCountDecrease(p);
+#endif
+ if (--p->ref_count <= 0) {
+ JS_FreeValueRTImpl(rt, v);
+ }
+ }
+}
+JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) {
+ (void)ctx;
+ return JS_MKVAL(JS_TAG_BOOL, (val != 0));
+}
+JSValue JS_NewInt32(JSContext *ctx, int32_t val) {
+ (void)ctx;
+ return JS_MKVAL(JS_TAG_INT, val);
+}
+JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) {
+ (void)ctx;
+ return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);
+}
+JSValue JS_NewInt64(JSContext *ctx, int64_t val) {
+ JSValue v;
+ if (val == (int32_t)val) {
+ v = JS_NewInt32(ctx, (int32_t)val);
+ } else {
+ v = JS_NewFloat64Impl(ctx, (double)val);
+ }
+ return v;
+}
+JSValue JS_NewUint32(JSContext *ctx, uint32_t val) {
+ JSValue v;
+ if (val <= 0x7fffffff) {
+ v = JS_NewInt32(ctx, val);
+ } else {
+ v = JS_NewFloat64Impl(ctx, val);
+ }
+ return v;
+}
+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_NewFloat64Impl(ctx, d);
+ }
+ return v;
+}
+JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
+ int length) {
+ return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0);
+}
+
+JS_BOOL JS_IsRegExp(JSContext *ctx, JSValue val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ return JS_VALUE_GET_OBJ(val)->class_id == JS_CLASS_REGEXP;
+}
diff --git a/src/shared/quickjs/quickjs.diff b/src/shared/quickjs/quickjs.diff
new file mode 100644
index 000000000..e87999050
--- /dev/null
+++ b/src/shared/quickjs/quickjs.diff
@@ -0,0 +1,4061 @@
+diff --git a/cutils.c b/cutils.c
+index a02fb76..1f66fff 100644
+--- a/cutils.c
++++ b/cutils.c
+@@ -166,8 +166,7 @@ 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, ...)
++int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...)
+ {
+ va_list ap;
+ char buf[128];
+diff --git a/cutils.h b/cutils.h
+index 31f7cd8..ee0ce4a 100644
+--- a/cutils.h
++++ b/cutils.h
+@@ -28,14 +28,33 @@
+ #include <stdlib.h>
+ #include <inttypes.h>
+
++#if defined(_MSC_VER)
++#include <BaseTsd.h>
++typedef SSIZE_T ssize_t;
++#else
++#include <sys/types.h>
++#endif
++
+ /* set if CPU is big endian */
+ #undef WORDS_BIGENDIAN
+
++#ifdef __GNUC__
+ #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 __maybe_unused __attribute__((unused))
++#define maybe_unused __attribute__((unused))
++#else
++#define likely(x) (x)
++#define unlikely(x) (x)
++#define force_inline
++#define no_inline
++#define maybe_unused
++#endif
++
++#ifdef _MSC_VER
++#define alloca _alloca
++#endif
+
+ #define xglue(x, y) x ## y
+ #define glue(x, y) xglue(x, y)
+@@ -114,38 +133,24 @@ static inline int64_t min_int64(int64_t a, int64_t b)
+ /* WARNING: undefined if a = 0 */
+ static inline int clz32(unsigned int a)
+ {
++#ifdef _MSC_VER
++ return (int) __lzcnt(a);
++#else
+ return __builtin_clz(a);
++#endif
+ }
+
+-/* 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 {
++#pragma pack(push, 1)
++struct packed_u64 {
+ uint64_t v;
+ };
+-
+-struct __attribute__((packed)) packed_u32 {
++struct packed_u32 {
+ uint32_t v;
+ };
+-
+-struct __attribute__((packed)) packed_u16 {
++struct packed_u16 {
+ uint16_t v;
+ };
++#pragma pack(pop)
+
+ static inline uint64_t get_u64(const uint8_t *tab)
+ {
+@@ -262,8 +267,15 @@ 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, ...);
++
++#ifdef __GNUC__
++#define FORMAT_ATTR(x, y) __attribute__((format(printf, x, y)))
++#else
++#define FORMAT_ATTR(x, y)
++#endif
++
++int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...);
++
+ void dbuf_free(DynBuf *s);
+ static inline BOOL dbuf_error(DynBuf *s) {
+ return s->error;
+diff --git a/libregexp.c b/libregexp.c
+index 379bfc7..ad91f78 100644
+--- a/libregexp.c
++++ b/libregexp.c
+@@ -271,7 +271,7 @@ static int cr_canonicalize(CharRange *cr)
+ }
+
+ #ifdef DUMP_REOP
+-static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
++static MAYBE_UNUSED void lre_dump_bytecode(const uint8_t *buf,
+ int buf_len)
+ {
+ int pos, len, opcode, bc_len, re_flags, i;
+@@ -427,7 +427,7 @@ static void re_emit_op_u16(REParseState *s, int op, uint32_t val)
+ dbuf_put_u16(&s->byte_code, val);
+ }
+
+-static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...)
++static int FORMAT_ATTR(2, 3) re_parse_error(REParseState *s, const char *fmt, ...)
+ {
+ va_list ap;
+ va_start(ap, fmt);
+@@ -1472,7 +1472,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
+ default:
+ parse_class_atom:
+ c = get_class_atom(s, cr, &p, FALSE);
+- if ((int)c < 0)
++ if (c < 0)
+ return -1;
+ normal_char:
+ last_atom_start = s->byte_code.size;
+@@ -1924,17 +1924,17 @@ static BOOL is_word_char(uint32_t c)
+ #define GET_CHAR(c, cptr, cbuf_end) \
+ do { \
+ if (cbuf_type == 0) { \
+- c = *cptr++; \
++ (c) = *(cptr)++; \
+ } else { \
+ uint32_t __c1; \
+- c = *(uint16_t *)cptr; \
+- cptr += 2; \
+- if (c >= 0xd800 && c < 0xdc00 && \
+- cbuf_type == 2 && cptr < cbuf_end) { \
+- __c1 = *(uint16_t *)cptr; \
++ (c) = *(uint16_t *)(cptr); \
++ (cptr) += 2; \
++ if ((c) >= 0xd800 && (c) < 0xdc00 && \
++ cbuf_type == 2 && (cptr) < (cbuf_end)) { \
++ __c1 = *(uint16_t *)(cptr); \
+ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
+- c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
+- cptr += 2; \
++ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
++ (cptr) += 2; \
+ } \
+ } \
+ } \
+@@ -1943,15 +1943,15 @@ static BOOL is_word_char(uint32_t c)
+ #define PEEK_CHAR(c, cptr, cbuf_end) \
+ do { \
+ if (cbuf_type == 0) { \
+- c = cptr[0]; \
++ (c) = (cptr)[0]; \
+ } else { \
+ uint32_t __c1; \
+- c = ((uint16_t *)cptr)[0]; \
+- if (c >= 0xd800 && c < 0xdc00 && \
+- cbuf_type == 2 && (cptr + 2) < cbuf_end) { \
+- __c1 = ((uint16_t *)cptr)[1]; \
++ (c) = ((uint16_t *)(cptr))[0]; \
++ if ((c) >= 0xd800 && (c) < 0xdc00 && \
++ cbuf_type == 2 && ((cptr) + 2) < (cbuf_end)) { \
++ __c1 = ((uint16_t *)(cptr))[1]; \
+ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
+- c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
++ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+@@ -1960,15 +1960,15 @@ static BOOL is_word_char(uint32_t c)
+ #define PEEK_PREV_CHAR(c, cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+- c = cptr[-1]; \
++ (c) = (cptr)[-1]; \
+ } else { \
+ uint32_t __c1; \
+- c = ((uint16_t *)cptr)[-1]; \
+- if (c >= 0xdc00 && c < 0xe000 && \
+- cbuf_type == 2 && (cptr - 4) >= cbuf_start) { \
+- __c1 = ((uint16_t *)cptr)[-2]; \
++ (c) = ((uint16_t *)(cptr))[-1]; \
++ if ((c) >= 0xdc00 && (c) < 0xe000 && \
++ cbuf_type == 2 && ((cptr) - 4) >= (cbuf_start)) { \
++ __c1 = ((uint16_t *)(cptr))[-2]; \
+ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
+- c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
++ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+@@ -1977,18 +1977,18 @@ static BOOL is_word_char(uint32_t c)
+ #define GET_PREV_CHAR(c, cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+- cptr--; \
+- c = cptr[0]; \
++ (cptr)--; \
++ (c) = (cptr)[0]; \
+ } else { \
+ uint32_t __c1; \
+- cptr -= 2; \
+- c = ((uint16_t *)cptr)[0]; \
+- if (c >= 0xdc00 && c < 0xe000 && \
+- cbuf_type == 2 && cptr > cbuf_start) { \
+- __c1 = ((uint16_t *)cptr)[-1]; \
++ (cptr) -= 2; \
++ (c) = ((uint16_t *)(cptr))[0]; \
++ if ((c) >= 0xdc00 && (c) < 0xe000 && \
++ cbuf_type == 2 && (cptr) > (cbuf_start)) { \
++ __c1 = ((uint16_t *)(cptr))[-1]; \
+ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
+- cptr -= 2; \
+- c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
++ (cptr) -= 2; \
++ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+@@ -1997,15 +1997,15 @@ static BOOL is_word_char(uint32_t c)
+ #define PREV_CHAR(cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+- cptr--; \
++ (cptr)--; \
+ } else { \
+- cptr -= 2; \
++ (cptr) -= 2; \
+ if (cbuf_type == 2) { \
+- c = ((uint16_t *)cptr)[0]; \
+- if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) { \
+- c = ((uint16_t *)cptr)[-1]; \
++ c = ((uint16_t *)(cptr))[0]; \
++ if (c >= 0xdc00 && c < 0xe000 && (cptr) > (cbuf_start)) { \
++ c = ((uint16_t *)(cptr))[-1]; \
+ if (c >= 0xd800 && c < 0xdc00) \
+- cptr -= 2; \
++ (cptr) -= 2; \
+ } \
+ } \
+ } \
+@@ -2049,7 +2049,7 @@ typedef struct {
+
+ static int push_state(REExecContext *s,
+ uint8_t **capture,
+- StackInt *stack, size_t stack_len,
++ const StackInt *stack, size_t stack_len,
+ const uint8_t *pc, const uint8_t *cptr,
+ REExecStateEnum type, size_t count)
+ {
+diff --git a/libunicode.c b/libunicode.c
+index 63c12a0..112da72 100644
+--- a/libunicode.c
++++ b/libunicode.c
+@@ -271,7 +271,7 @@ BOOL lre_is_case_ignorable(uint32_t c)
+
+ /* character range */
+
+-static __maybe_unused void cr_dump(CharRange *cr)
++static maybe_unused void cr_dump(CharRange *cr)
+ {
+ int i;
+ for(i = 0; i < cr->len; i++)
+@@ -1315,11 +1315,13 @@ static int unicode_prop_ops(CharRange *cr, ...)
+ }
+ }
+ done:
++ va_end(ap);
+ assert(stack_len == 1);
+ ret = cr_copy(cr, &stack[0]);
+ cr_free(&stack[0]);
+ return ret;
+ fail:
++ va_end(ap);
+ for(i = 0; i < stack_len; i++)
+ cr_free(&stack[i]);
+ return -1;
+diff --git a/list.h b/list.h
+index 0a1bc5a..e7f51a9 100644
+--- a/list.h
++++ b/list.h
+@@ -46,7 +46,7 @@ static inline void init_list_head(struct list_head *head)
+ }
+
+ /* insert 'el' between 'prev' and 'next' */
+-static inline void __list_add(struct list_head *el,
++static inline void list_add_impl(struct list_head *el,
+ struct list_head *prev, struct list_head *next)
+ {
+ prev->next = el;
+@@ -58,13 +58,13 @@ static inline void __list_add(struct list_head *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);
++ list_add_impl(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);
++ list_add_impl(el, head->prev, head);
+ }
+
+ static inline void list_del(struct list_head *el)
+diff --git a/quickjs.c b/quickjs.c
+index 7916013..f90fb9e 100644
+--- a/quickjs.c
++++ b/quickjs.c
+@@ -28,7 +28,9 @@
+ #include <inttypes.h>
+ #include <string.h>
+ #include <assert.h>
++#ifndef _MSC_VER
+ #include <sys/time.h>
++#endif
+ #include <time.h>
+ #include <fenv.h>
+ #include <math.h>
+@@ -40,6 +42,18 @@
+ #include <malloc_np.h>
+ #endif
+
++#ifdef _MSC_VER
++#include <intrin.h>
++#include <windows.h>
++#endif
++
++#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
++ || defined(__APPLE__)
++#include <xlocale.h>
++#else
++#include <locale.h>
++#endif
++
+ #include "cutils.h"
+ #include "list.h"
+ #include "quickjs.h"
+@@ -48,9 +62,9 @@
+ #include "libbf.h"
+ #endif
+
+-#define OPTIMIZE 1
++#define OPTIMIZE 0
+ #define SHORT_OPCODES 1
+-#if defined(EMSCRIPTEN)
++#if defined(EMSCRIPTEN) || defined(_MSC_VER)
+ #define DIRECT_DISPATCH 0
+ #else
+ #define DIRECT_DISPATCH 1
+@@ -69,7 +83,7 @@
+
+ /* define to include Atomics.* operations which depend on the OS
+ threads */
+-#if !defined(EMSCRIPTEN)
++#if !defined(EMSCRIPTEN) && !defined(_MSC_VER)
+ #define CONFIG_ATOMICS
+ #endif
+
+@@ -78,7 +92,6 @@
+ #define CONFIG_STACK_CHECK
+ #endif
+
+-
+ /* dump object free */
+ //#define DUMP_FREE
+ //#define DUMP_CLOSURE
+@@ -115,6 +128,25 @@
+ #include <errno.h>
+ #endif
+
++static double safe_strtod(const char *restrict nptr, char **restrict endptr)
++{
++#if defined(_MSC_VER) || defined(__MINGW32__)
++ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
++ setlocale(LC_NUMERIC, "C");
++#else
++ const locale_t tempLoc = newlocale(LC_NUMERIC_MASK, "C", 0);
++ uselocale(tempLoc);
++#endif
++ double d = strtod(nptr, endptr);
++#if defined(_MSC_VER) || defined(__MINGW32__)
++ _configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
++#else
++ uselocale(LC_GLOBAL_LOCALE);
++ freelocale(tempLoc);
++#endif
++ return d;
++}
++
+ enum {
+ /* classid tag */ /* union usage | properties */
+ JS_CLASS_OBJECT = 1, /* must be first */
+@@ -204,7 +236,11 @@ typedef enum JSErrorEnum {
+ #define JS_STACK_SIZE_MAX 65534
+ #define JS_STRING_LEN_MAX ((1 << 30) - 1)
+
+-#define __exception __attribute__((warn_unused_result))
++#ifdef __GNUC__
++#define warn_unused __attribute__((warn_unused_result))
++#else
++#define warn_unused
++#endif
+
+ typedef struct JSShape JSShape;
+ typedef struct JSString JSString;
+@@ -362,8 +398,8 @@ typedef struct JSVarRef {
+ union {
+ JSGCObjectHeader header; /* must come first */
+ struct {
+- int __gc_ref_count; /* corresponds to header.ref_count */
+- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
++ int _gc_ref_count; /* corresponds to header.ref_count */
++ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */
+
+ /* 0 : the JSVarRef is on the stack. header.link is an element
+ of JSStackFrame.var_ref_list.
+@@ -455,8 +491,12 @@ struct JSContext {
+ /* if NULL, eval is not supported */
+ JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int flags, int scope_idx);
++ const char *filename, int line, int flags, int scope_idx);
+ void *user_opaque;
++ ScopeLookup *scopeLookup;
++ FoundUndefinedHandler *handleUndefined;
++ FunctionEnteredHandler *handleFunctionEntered;
++ FunctionExitedHandler *handleFunctionExited;
+ };
+
+ typedef union JSFloat64Union {
+@@ -867,8 +907,8 @@ struct JSObject {
+ union {
+ JSGCObjectHeader header;
+ struct {
+- int __gc_ref_count; /* corresponds to header.ref_count */
+- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
++ int _gc_ref_count; /* corresponds to header.ref_count */
++ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */
+
+ uint8_t extensible : 1;
+ uint8_t free_mark : 1; /* only used when freeing objects with cycles */
+@@ -950,7 +990,7 @@ struct JSObject {
+ /* byte sizes: 40/48/72 */
+ };
+ enum {
+- __JS_ATOM_NULL = JS_ATOM_NULL,
++ JS_ATOM_NULL_ = JS_ATOM_NULL,
+ #define DEF(name, str) JS_ATOM_ ## name,
+ #include "quickjs-atom.h"
+ #undef DEF
+@@ -996,7 +1036,7 @@ enum OPCodeEnum {
+ };
+
+ static int JS_InitAtoms(JSRuntime *rt);
+-static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
++static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len,
+ int atom_type);
+ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
+ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
+@@ -1017,24 +1057,23 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o
+ int argc, JSValueConst *argv);
+ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
+ int argc, JSValueConst *argv);
+-static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
++static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor);
+ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+ JSValueConst val, int flags, int scope_idx);
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
+-static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
+-static __maybe_unused void JS_DumpString(JSRuntime *rt,
++static maybe_unused void JS_DumpAtoms(JSRuntime *rt);
++static maybe_unused void JS_DumpString(JSRuntime *rt,
+ const JSString *p);
+-static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
+-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
+-static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
+-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
++static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
++static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
++static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
++static maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val);
+-static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
+-static __maybe_unused void JS_PrintValue(JSContext *ctx,
++static maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
++static maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val);
+-static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
++static maybe_unused void JS_DumpShapes(JSRuntime *rt);
+ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic);
+ static void js_array_finalizer(JSRuntime *rt, JSValue val);
+@@ -1148,7 +1187,6 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
+ BOOL allow_null_or_undefined);
+ static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
+ #endif
+-JSValue JS_ThrowOutOfMemory(JSContext *ctx);
+ static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
+ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
+ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
+@@ -1187,7 +1225,7 @@ static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+ static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int flags, int scope_idx);
++ const char *filename, int line, int flags, int scope_idx);
+ static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
+ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
+ JS_MarkFunc *mark_func);
+@@ -1197,7 +1235,7 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
+ static JSValue js_new_promise_capability(JSContext *ctx,
+ JSValue *resolving_funcs,
+ JSValueConst ctor);
+-static __exception int perform_promise_then(JSContext *ctx,
++static warn_unused int perform_promise_then(JSContext *ctx,
+ JSValueConst promise,
+ JSValueConst *resolve_reject,
+ JSValueConst *cap_resolving_funcs);
+@@ -1222,9 +1260,9 @@ static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
+ static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
+ JSShapeProperty **pprs);
+ static int init_shape_hash(JSRuntime *rt);
+-static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
++static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres,
+ JSValueConst obj);
+-static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
++static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres,
+ JSValueConst obj);
+ static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
+ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
+@@ -1582,10 +1620,27 @@ static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
+ return FALSE;
+ }
+ #else
+-/* Note: OS and CPU dependent */
++// Uses code from LLVM project.
+ static inline uintptr_t js_get_stack_pointer(void)
+ {
++#ifdef _MSC_VER
++ return (uintptr_t) _AddressOfReturnAddress();
++#elif defined __has_builtin
++#if __has_builtin(__builtin_frame_address)
+ return (uintptr_t) __builtin_frame_address(0);
++#endif
++#elif defined __GNUC__
++ return (uintptr_t) __builtin_frame_address(0);
++#else
++ char CharOnStack = 0;
++ // The volatile store here is intended to escape the local variable, to
++ // prevent the compiler from optimizing CharOnStack into anything other
++ // than a char on the stack.
++ //
++ // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19.
++ char *volatile Ptr = &CharOnStack;
++ return (uintptr_t) Ptr;
++#endif
+ }
+
+ static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
+@@ -1980,6 +2035,7 @@ void JS_FreeRuntime(JSRuntime *rt)
+ printf("Secondary object leaks: %d\n", count);
+ }
+ #endif
++ fflush(stdout);
+ assert(list_empty(&rt->gc_obj_list));
+
+ /* free the classes */
+@@ -2386,7 +2442,7 @@ static inline BOOL is_math_mode(JSContext *ctx)
+ /* return the max count from the hash size */
+ #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
+
+-static inline BOOL __JS_AtomIsConst(JSAtom v)
++static inline BOOL JS_AtomIsConst(JSAtom v)
+ {
+ #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
+ return (int32_t)v <= 0;
+@@ -2395,17 +2451,17 @@ static inline BOOL __JS_AtomIsConst(JSAtom v)
+ #endif
+ }
+
+-static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)
++static inline BOOL JS_AtomIsTaggedInt(JSAtom v)
+ {
+ return (v & JS_ATOM_TAG_INT) != 0;
+ }
+
+-static inline JSAtom __JS_AtomFromUInt32(uint32_t v)
++static inline JSAtom JS_AtomFromUInt32(uint32_t v)
+ {
+ return v | JS_ATOM_TAG_INT;
+ }
+
+-static inline uint32_t __JS_AtomToUInt32(JSAtom atom)
++static inline uint32_t JS_AtomToUInt32(JSAtom atom)
+ {
+ return atom & ~JS_ATOM_TAG_INT;
+ }
+@@ -2485,7 +2541,7 @@ static uint32_t hash_string(const JSString *str, uint32_t h)
+ return h;
+ }
+
+-static __maybe_unused void JS_DumpString(JSRuntime *rt,
++static maybe_unused void JS_DumpString(JSRuntime *rt,
+ const JSString *p)
+ {
+ int i, c, sep;
+@@ -2517,7 +2573,7 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt,
+ putchar(sep);
+ }
+
+-static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)
++static maybe_unused void JS_DumpAtoms(JSRuntime *rt)
+ {
+ JSAtomStruct *p;
+ int h, i;
+@@ -2604,7 +2660,7 @@ static int JS_InitAtoms(JSRuntime *rt)
+ else
+ atom_type = JS_ATOM_TYPE_STRING;
+ len = strlen(p);
+- if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)
++ if (JS_NewAtomInitImpl(rt, p, len, atom_type) == JS_ATOM_NULL)
+ return -1;
+ p = p + len + 1;
+ }
+@@ -2615,7 +2671,7 @@ static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
+ {
+ JSAtomStruct *p;
+
+- if (!__JS_AtomIsConst(v)) {
++ if (!JS_AtomIsConst(v)) {
+ p = rt->atom_array[v];
+ p->header.ref_count++;
+ }
+@@ -2627,7 +2683,7 @@ JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
+ JSRuntime *rt;
+ JSAtomStruct *p;
+
+- if (!__JS_AtomIsConst(v)) {
++ if (!JS_AtomIsConst(v)) {
+ rt = ctx->rt;
+ p = rt->atom_array[v];
+ p->header.ref_count++;
+@@ -2641,7 +2697,7 @@ static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
+ JSAtomStruct *p;
+
+ rt = ctx->rt;
+- if (__JS_AtomIsTaggedInt(v))
++ if (JS_AtomIsTaggedInt(v))
+ return JS_ATOM_KIND_STRING;
+ p = rt->atom_array[v];
+ switch(p->atom_type) {
+@@ -2687,7 +2743,7 @@ static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
+
+ /* string case (internal). Return JS_ATOM_NULL if error. 'str' is
+ freed. */
+-static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
++static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type)
+ {
+ uint32_t h, h1, i;
+ JSAtomStruct *p;
+@@ -2702,7 +2758,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
+ /* str is the atom, return its index */
+ i = js_get_atom_index(rt, str);
+ /* reduce string refcount and increase atom's unless constant */
+- if (__JS_AtomIsConst(i))
++ if (JS_AtomIsConst(i))
+ str->header.ref_count--;
+ return i;
+ }
+@@ -2718,7 +2774,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
+ p->atom_type == atom_type &&
+ p->len == len &&
+ js_string_memcmp(p, str, len) == 0) {
+- if (!__JS_AtomIsConst(i))
++ if (!JS_AtomIsConst(i))
+ p->header.ref_count++;
+ goto done;
+ }
+@@ -2843,7 +2899,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
+ }
+
+ /* only works with zero terminated 8 bit strings */
+-static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
++static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len,
+ int atom_type)
+ {
+ JSString *p;
+@@ -2852,10 +2908,10 @@ static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
+ return JS_ATOM_NULL;
+ memcpy(p->u.str8, str, len);
+ p->u.str8[len] = '\0';
+- return __JS_NewAtom(rt, p, atom_type);
++ return JS_NewAtomImpl(rt, p, atom_type);
+ }
+
+-static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
++static JSAtom JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
+ int atom_type)
+ {
+ uint32_t h, h1, i;
+@@ -2872,7 +2928,7 @@ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
+ p->len == len &&
+ p->is_wide_char == 0 &&
+ memcmp(p->u.str8, str, len) == 0) {
+- if (!__JS_AtomIsConst(i))
++ if (!JS_AtomIsConst(i))
+ p->header.ref_count++;
+ return i;
+ }
+@@ -2924,7 +2980,7 @@ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
+ assert(rt->atom_count >= 0);
+ }
+
+-static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
++static void JS_FreeAtomImpl(JSRuntime *rt, uint32_t i)
+ {
+ JSAtomStruct *p;
+
+@@ -2942,11 +2998,11 @@ static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
+ if (is_num_string(&n, p)) {
+ if (n <= JS_ATOM_MAX_INT) {
+ js_free_string(rt, p);
+- return __JS_AtomFromUInt32(n);
++ return JS_AtomFromUInt32(n);
+ }
+ }
+ /* XXX: should generate an exception */
+- return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
++ return JS_NewAtomImpl(rt, p, JS_ATOM_TYPE_STRING);
+ }
+
+ JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
+@@ -2954,7 +3010,7 @@ JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
+ JSValue val;
+
+ if (len == 0 || !is_digit(*str)) {
+- JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
++ JSAtom atom = JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
+ if (atom)
+ return atom;
+ }
+@@ -2972,7 +3028,7 @@ JSAtom JS_NewAtom(JSContext *ctx, const char *str)
+ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
+ {
+ if (n <= JS_ATOM_MAX_INT) {
+- return __JS_AtomFromUInt32(n);
++ return JS_AtomFromUInt32(n);
+ } else {
+ char buf[11];
+ JSValue val;
+@@ -2980,7 +3036,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
+ val = JS_NewString(ctx, buf);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
++ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val),
+ JS_ATOM_TYPE_STRING);
+ }
+ }
+@@ -2988,7 +3044,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
+ static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
+ {
+ if ((uint64_t)n <= JS_ATOM_MAX_INT) {
+- return __JS_AtomFromUInt32((uint32_t)n);
++ return JS_AtomFromUInt32((uint32_t)n);
+ } else {
+ char buf[24];
+ JSValue val;
+@@ -2996,7 +3052,7 @@ static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
+ val = JS_NewString(ctx, buf);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
++ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val),
+ JS_ATOM_TYPE_STRING);
+ }
+ }
+@@ -3006,7 +3062,7 @@ static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
+ {
+ JSRuntime *rt = ctx->rt;
+ JSAtom atom;
+- atom = __JS_NewAtom(rt, p, atom_type);
++ atom = JS_NewAtomImpl(rt, p, atom_type);
+ if (atom == JS_ATOM_NULL)
+ return JS_ThrowOutOfMemory(ctx);
+ return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
+@@ -3019,7 +3075,7 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
+ JSRuntime *rt = ctx->rt;
+ JSString *p;
+
+- assert(!__JS_AtomIsTaggedInt(descr));
++ assert(!JS_AtomIsTaggedInt(descr));
+ assert(descr < rt->atom_size);
+ p = rt->atom_array[descr];
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+@@ -3032,8 +3088,8 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
+ static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
+ JSAtom atom)
+ {
+- if (__JS_AtomIsTaggedInt(atom)) {
+- snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
++ if (JS_AtomIsTaggedInt(atom)) {
++ snprintf(buf, buf_size, "%u", JS_AtomToUInt32(atom));
+ } else {
+ JSAtomStruct *p;
+ assert(atom < rt->atom_size);
+@@ -3083,12 +3139,12 @@ static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom
+ return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
+ }
+
+-static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
++static JSValue JS_AtomToValueImpl(JSContext *ctx, JSAtom atom, BOOL force_string)
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+
+- if (__JS_AtomIsTaggedInt(atom)) {
+- snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));
++ if (JS_AtomIsTaggedInt(atom)) {
++ snprintf(buf, sizeof(buf), "%u", JS_AtomToUInt32(atom));
+ return JS_NewString(ctx, buf);
+ } else {
+ JSRuntime *rt = ctx->rt;
+@@ -3112,20 +3168,20 @@ static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
+
+ JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
+ {
+- return __JS_AtomToValue(ctx, atom, FALSE);
++ return JS_AtomToValueImpl(ctx, atom, FALSE);
+ }
+
+ JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
+ {
+- return __JS_AtomToValue(ctx, atom, TRUE);
++ return JS_AtomToValueImpl(ctx, atom, TRUE);
+ }
+
+ /* return TRUE if the atom is an array index (i.e. 0 <= index <=
+ 2^32-2 and return its value */
+ static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
+ {
+- if (__JS_AtomIsTaggedInt(atom)) {
+- *pval = __JS_AtomToUInt32(atom);
++ if (JS_AtomIsTaggedInt(atom)) {
++ *pval = JS_AtomToUInt32(atom);
+ return TRUE;
+ } else {
+ JSRuntime *rt = ctx->rt;
+@@ -3156,8 +3212,8 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
+ int c, len, ret;
+ JSValue num, str;
+
+- if (__JS_AtomIsTaggedInt(atom))
+- return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
++ if (JS_AtomIsTaggedInt(atom))
++ return JS_NewInt32(ctx, JS_AtomToUInt32(atom));
+ assert(atom < rt->atom_size);
+ p1 = rt->atom_array[atom];
+ if (p1->atom_type != JS_ATOM_TYPE_STRING)
+@@ -3199,7 +3255,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
+ /* -0 case is specific */
+ if (c == '0' && len == 2) {
+ minus_zero:
+- return __JS_NewFloat64(ctx, -0.0);
++ return JS_NewFloat64Impl(ctx, -0.0);
+ }
+ }
+ if (!is_num(c)) {
+@@ -3244,14 +3300,14 @@ static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
+
+ void JS_FreeAtom(JSContext *ctx, JSAtom v)
+ {
+- if (!__JS_AtomIsConst(v))
+- __JS_FreeAtom(ctx->rt, v);
++ if (!JS_AtomIsConst(v))
++ JS_FreeAtomImpl(ctx->rt, v);
+ }
+
+ void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
+ {
+- if (!__JS_AtomIsConst(v))
+- __JS_FreeAtom(rt, v);
++ if (!JS_AtomIsConst(v))
++ JS_FreeAtomImpl(rt, v);
+ }
+
+ /* return TRUE if 'v' is a symbol with a string description */
+@@ -3261,7 +3317,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
+ JSAtomStruct *p;
+
+ rt = ctx->rt;
+- if (__JS_AtomIsTaggedInt(v))
++ if (JS_AtomIsTaggedInt(v))
+ return FALSE;
+ p = rt->atom_array[v];
+ return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
+@@ -3270,7 +3326,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
+ !(p->len == 0 && p->is_wide_char != 0));
+ }
+
+-static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
++static maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ const char *p;
+@@ -3445,9 +3501,9 @@ int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
+ JSAtom name;
+
+ len = strlen(class_def->class_name);
+- name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
++ name = JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
+ if (name == JS_ATOM_NULL) {
+- name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
++ name = JS_NewAtomInitImpl(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
+ if (name == JS_ATOM_NULL)
+ return -1;
+ }
+@@ -4614,7 +4670,7 @@ static int add_shape_property(JSContext *ctx, JSShape **psh,
+ pr = &prop[sh->prop_count++];
+ pr->atom = JS_DupAtom(ctx, atom);
+ pr->flags = prop_flags;
+- sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
++ sh->has_small_array_index |= JS_AtomIsTaggedInt(atom);
+ /* add in hash table */
+ hash_mask = sh->prop_hash_mask;
+ h = atom & hash_mask;
+@@ -4675,7 +4731,7 @@ static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
+ return NULL;
+ }
+
+-static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
++static maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
+ {
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ int j;
+@@ -4691,7 +4747,7 @@ static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
+ printf("\n");
+ }
+
+-static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
++static maybe_unused void JS_DumpShapes(JSRuntime *rt)
+ {
+ int i;
+ JSShape *sh;
+@@ -5471,7 +5527,7 @@ static void free_zero_refcount(JSRuntime *rt)
+ }
+
+ /* called with the ref_count of 'v' reaches zero. */
+-void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
++static void JS_FreeValueRTImpl(JSRuntime *rt, JSValue v)
+ {
+ uint32_t tag = JS_VALUE_GET_TAG(v);
+
+@@ -5546,9 +5602,9 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
+ }
+ }
+
+-void __JS_FreeValue(JSContext *ctx, JSValue v)
++static void JS_FreeValueImpl(JSContext *ctx, JSValue v)
+ {
+- __JS_FreeValueRT(ctx->rt, v);
++ JS_FreeValueRTImpl(ctx->rt, v);
+ }
+
+ /* garbage collection */
+@@ -6469,7 +6525,7 @@ static const char *get_func_name(JSContext *ctx, JSValueConst func)
+
+ /* if filename != NULL, an additional level is added with the filename
+ and line number information (used for parse error). */
+-static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
++void build_backtrace(JSContext *ctx, JSValueConst error_obj,
+ const char *filename, int line_num,
+ int backtrace_flags)
+ {
+@@ -6569,7 +6625,7 @@ JSValue JS_NewError(JSContext *ctx)
+ static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
+ const char *fmt, va_list ap, BOOL add_backtrace)
+ {
+- char buf[256];
++ char buf[8192];
+ JSValue obj, ret;
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+@@ -6604,7 +6660,7 @@ static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
+ return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6615,7 +6671,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx
+ return val;
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6626,7 +6682,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx,
+ return val;
+ }
+
+-static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
++static int FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
+ {
+ va_list ap;
+
+@@ -6642,7 +6698,7 @@ static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSCont
+ }
+
+ /* never use it directly */
+-static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
++static JSValue FORMAT_ATTR(3, 4) JS_ThrowTypeErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowTypeError(ctx, fmt,
+@@ -6650,7 +6706,7 @@ static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSC
+ }
+
+ /* never use it directly */
+-static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
++static JSValue FORMAT_ATTR(3, 4) JS_ThrowSyntaxErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowSyntaxError(ctx, fmt,
+@@ -6659,8 +6715,8 @@ static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(J
+
+ /* %s is replaced by 'atom'. The macro is used so that gcc can check
+ the format string. */
+-#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")
+-#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")
++#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) JS_ThrowTypeErrorAtomImpl(ctx, atom, fmt, "")
++#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) JS_ThrowSyntaxErrorAtomImpl(ctx, atom, fmt, "")
+
+ static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
+ {
+@@ -6673,7 +6729,7 @@ static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
+ }
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6684,7 +6740,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *
+ return val;
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6695,7 +6751,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx,
+ return val;
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6770,7 +6826,7 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
+ return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
+ }
+
+-static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
++static no_inline warn_unused int js_poll_interrupts_impl(JSContext *ctx)
+ {
+ JSRuntime *rt = ctx->rt;
+ ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
+@@ -6785,10 +6841,10 @@ static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
+ return 0;
+ }
+
+-static inline __exception int js_poll_interrupts(JSContext *ctx)
++static inline warn_unused int js_poll_interrupts(JSContext *ctx)
+ {
+ if (unlikely(--ctx->interrupt_counter <= 0)) {
+- return __js_poll_interrupts(ctx);
++ return js_poll_interrupts_impl(ctx);
+ } else {
+ return 0;
+ }
+@@ -7092,9 +7148,9 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
+ case JS_TAG_STRING:
+ {
+ JSString *p1 = JS_VALUE_GET_STRING(obj);
+- if (__JS_AtomIsTaggedInt(prop)) {
++ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx, ch;
+- idx = __JS_AtomToUInt32(prop);
++ idx = JS_AtomToUInt32(prop);
+ if (idx < p1->len) {
+ if (p1->is_wide_char)
+ ch = p1->u.str16[idx];
+@@ -7144,14 +7200,16 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
+ continue;
+ }
+ } else {
++ if (JS_IsUndefined(pr->u.value) && ctx->handleUndefined)
++ ctx->handleUndefined(ctx);
+ return JS_DupValue(ctx, pr->u.value);
+ }
+ }
+ if (unlikely(p->is_exotic)) {
+ /* exotic behaviors */
+ if (p->fast_array) {
+- if (__JS_AtomIsTaggedInt(prop)) {
+- uint32_t idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ /* we avoid duplicating the code */
+ return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
+@@ -7242,7 +7300,7 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
+ JS_ThrowTypeErrorNotASymbol(ctx);
+ goto fail;
+ }
+- prop = js_symbol_to_atom(ctx, (JSValue)name);
++ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+@@ -7273,7 +7331,7 @@ static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
+ return JS_ThrowTypeErrorNotASymbol(ctx);
+- prop = js_symbol_to_atom(ctx, (JSValue)name);
++ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (!prs) {
+@@ -7300,7 +7358,7 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
+ JS_ThrowTypeErrorNotASymbol(ctx);
+ goto fail;
+ }
+- prop = js_symbol_to_atom(ctx, (JSValue)name);
++ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (!prs) {
+@@ -7390,7 +7448,7 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ goto not_obj;
+ p = JS_VALUE_GET_OBJ(obj);
+- prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
++ prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand));
+ if (!prs) {
+ JS_ThrowTypeError(ctx, "invalid brand on object");
+ return -1;
+@@ -7445,7 +7503,7 @@ static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
+
+ /* return < 0 in case if exception, 0 if OK. ptab and its atoms must
+ be freed by the user. */
+-static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
++static int warn_unused JS_GetOwnPropertyNamesInternal(JSContext *ctx,
+ JSPropertyEnum **ptab,
+ uint32_t *plen,
+ JSObject *p, int flags)
+@@ -7595,7 +7653,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
+ len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ add_array_keys:
+ for(i = 0; i < len; i++) {
+- tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
++ tab_atom[num_index].atom = JS_AtomFromUInt32(i);
+ if (tab_atom[num_index].atom == JS_ATOM_NULL) {
+ js_free_prop_enum(ctx, tab_atom, num_index);
+ return -1;
+@@ -7703,9 +7761,9 @@ retry:
+ if (p->is_exotic) {
+ if (p->fast_array) {
+ /* specific case for fast arrays */
+- if (__JS_AtomIsTaggedInt(prop)) {
++ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx;
+- idx = __JS_AtomToUInt32(prop);
++ idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ if (desc) {
+ desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
+@@ -7825,7 +7883,7 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
+ if (tag == JS_TAG_INT &&
+ (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
+ /* fast path for integer values */
+- atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
++ atom = JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
+ } else if (tag == JS_TAG_SYMBOL) {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
+@@ -7856,7 +7914,7 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ /* fast path for array access */
+ p = JS_VALUE_GET_OBJ(this_obj);
+ idx = JS_VALUE_GET_INT(prop);
+- len = (uint32_t)p->u.array.count;
++ len = p->u.array.count;
+ if (unlikely(idx >= len))
+ goto slow_path;
+ switch(p->class_id) {
+@@ -7883,9 +7941,9 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
+ #endif
+ case JS_CLASS_FLOAT32_ARRAY:
+- return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
++ return JS_NewFloat64Impl(ctx, p->u.array.u.float_ptr[idx]);
+ case JS_CLASS_FLOAT64_ARRAY:
+- return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);
++ return JS_NewFloat64Impl(ctx, p->u.array.u.double_ptr[idx]);
+ default:
+ goto slow_path;
+ }
+@@ -7920,7 +7978,7 @@ static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx,
+
+ if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
+ /* fast path */
+- present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
++ present = JS_HasProperty(ctx, obj, JS_AtomFromUInt32(idx));
+ if (present > 0) {
+ val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
+ if (unlikely(JS_IsException(val)))
+@@ -8017,7 +8075,7 @@ static JSProperty *add_property(JSContext *ctx,
+
+ /* can be called on Array or Arguments objects. return < 0 if
+ memory alloc error. */
+-static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
++static no_inline warn_unused int convert_fast_array_to_array(JSContext *ctx,
+ JSObject *p)
+ {
+ JSProperty *pr;
+@@ -8039,8 +8097,8 @@ static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
+ tab = p->u.array.u.values;
+ for(i = 0; i < len; i++) {
+ /* add_property cannot fail here but
+- __JS_AtomFromUInt32(i) fails for i > INT32_MAX */
+- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);
++ JS_AtomFromUInt32(i) fails for i > INT32_MAX */
++ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E);
+ pr->u.value = *tab++;
+ }
+ js_free(ctx, p->u.array.u.values);
+@@ -8145,7 +8203,7 @@ static int call_setter(JSContext *ctx, JSObject *setter,
+ func = JS_MKPTR(JS_TAG_OBJECT, setter);
+ /* Note: the field could be removed in the setter */
+ func = JS_DupValue(ctx, func);
+- ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);
++ ret = JS_CallFree(ctx, func, this_obj, 1, &val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(ret))
+ return -1;
+@@ -8489,8 +8547,8 @@ retry:
+ for(;;) {
+ if (p1->is_exotic) {
+ if (p1->fast_array) {
+- if (__JS_AtomIsTaggedInt(prop)) {
+- uint32_t idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx < p1->u.array.count) {
+ if (unlikely(p == p1))
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
+@@ -8610,8 +8668,8 @@ retry:
+
+ if (p->is_exotic) {
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
+- __JS_AtomIsTaggedInt(prop)) {
+- uint32_t idx = __JS_AtomToUInt32(prop);
++ JS_AtomIsTaggedInt(prop)) {
++ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ /* fast case */
+ return add_fast_array_element(ctx, p, val, flags);
+@@ -8662,7 +8720,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSShape *sh1;
+
+ /* fast path to add an element to the array */
+- if (idx != (uint32_t)p->u.array.count ||
++ if (idx != p->u.array.count ||
+ !p->fast_array || !p->extensible)
+ goto slow_path;
+ /* check if prototype chain has a numeric property */
+@@ -8837,8 +8895,8 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
+ uint32_t idx, len;
+
+ if (p->fast_array) {
+- if (__JS_AtomIsTaggedInt(prop)) {
+- idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ idx = JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ if (!p->extensible)
+ goto not_extensible;
+@@ -9042,7 +9100,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ return -1;
+ }
+ /* this code relies on the fact that Uint32 are never allocated */
+- val = (JSValueConst)JS_NewUint32(ctx, array_length);
++ val = JS_NewUint32(ctx, array_length);
+ /* prs may have been modified */
+ prs = find_own_property(&pr, p, prop);
+ assert(prs != NULL);
+@@ -9214,8 +9272,8 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx;
+ uint32_t prop_flags;
+ if (p->class_id == JS_CLASS_ARRAY) {
+- if (__JS_AtomIsTaggedInt(prop)) {
+- idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
+ if (prop_flags != JS_PROP_C_W_E)
+@@ -9238,7 +9296,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ JSValue num;
+ int ret;
+
+- if (!__JS_AtomIsTaggedInt(prop)) {
++ if (!JS_AtomIsTaggedInt(prop)) {
+ /* slow path with to handle all numeric indexes */
+ num = JS_AtomIsNumericIndex1(ctx, prop);
+ if (JS_IsUndefined(num))
+@@ -9259,10 +9317,10 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ if (ret) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
+ }
+- if (!__JS_AtomIsTaggedInt(prop))
++ if (!JS_AtomIsTaggedInt(prop))
+ goto typed_array_oob;
+ }
+- idx = __JS_AtomToUInt32(prop);
++ idx = JS_AtomToUInt32(prop);
+ /* if the typed array is detached, p->u.array.count = 0 */
+ if (idx >= typed_array_get_length(ctx, p)) {
+ typed_array_oob:
+@@ -9563,6 +9621,11 @@ static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
+ return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return JS_DupValue(ctx, pr->u.value);
+ }
++ if (ctx->scopeLookup) {
++ struct LookupResult result = ctx->scopeLookup(ctx, prop);
++ if (result.useResult)
++ return result.value;
++ }
+ return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
+ ctx->global_obj, throw_ref_error);
+ }
+@@ -9658,6 +9721,15 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
+ flags = JS_PROP_THROW_STRICT;
+ if (is_strict_mode(ctx))
+ flags |= JS_PROP_NO_ADD;
++
++ if (ctx->scopeLookup) {
++ struct LookupResult result = ctx->scopeLookup(ctx, prop);
++ if (result.useResult) {
++ JS_FreeValue(ctx, result.value);
++ return JS_SetPropertyInternal(ctx, result.scope, prop, val, flags);
++ }
++ }
++
+ return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);
+ }
+
+@@ -9693,7 +9765,7 @@ int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int fl
+
+ if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
+ /* fast path for fast arrays */
+- return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);
++ return JS_DeleteProperty(ctx, obj, JS_AtomFromUInt32(idx), flags);
+ }
+ prop = JS_NewAtomInt64(ctx, idx);
+ if (prop == JS_ATOM_NULL)
+@@ -9853,7 +9925,7 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
+ break;
+ }
+ arg = JS_AtomToString(ctx, atom);
+- ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
++ ret = JS_CallFree(ctx, method, val, 1, &arg);
+ JS_FreeValue(ctx, arg);
+ if (JS_IsException(ret))
+ goto exception;
+@@ -10057,7 +10129,7 @@ static double js_strtod(const char *p, int radix, BOOL is_float)
+ if (is_neg)
+ d = -d;
+ } else {
+- d = strtod(p, NULL);
++ d = safe_strtod(p, NULL);
+ }
+ return d;
+ }
+@@ -10237,7 +10309,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ } else
+ #endif
+ {
+- double d = 1.0 / 0.0;
++ double d = INFINITY;
+ if (is_neg)
+ d = -d;
+ val = JS_NewFloat64(ctx, d);
+@@ -10270,11 +10342,8 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
+ const char *p1 = p + 1;
+ is_float = TRUE;
+- if (*p1 == '+') {
+- p1++;
+- } else if (*p1 == '-') {
++ if (*p1 == '+' || *p1 == '-')
+ p1++;
+- }
+ if (is_digit((uint8_t)*p1)) {
+ p = p1 + 1;
+ while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
+@@ -10509,7 +10578,7 @@ static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
+ return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
+ }
+
+-static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
++static warn_unused int JS_ToFloat64FreeImpl(JSContext *ctx, double *pres,
+ JSValue val)
+ {
+ double d;
+@@ -10560,7 +10629,7 @@ static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
+ *pres = JS_VALUE_GET_FLOAT64(val);
+ return 0;
+ } else {
+- return __JS_ToFloat64Free(ctx, pres, val);
++ return JS_ToFloat64FreeImpl(ctx, pres, val);
+ }
+ }
+
+@@ -10575,7 +10644,7 @@ static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
+ }
+
+ /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
+-static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
++static maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
+ {
+ uint32_t tag;
+ JSValue ret;
+@@ -10990,7 +11059,7 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
+ return 0;
+ }
+
+-static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
++static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor)
+ {
+ uint32_t tag, len;
+@@ -11092,7 +11161,7 @@ int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
+
+ /* convert a value to a length between 0 and MAX_SAFE_INTEGER.
+ return -1 for exception */
+-static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
++static warn_unused int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
+ JSValue val)
+ {
+ int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
+@@ -11338,7 +11407,7 @@ static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
+ n_digits = (n_digits_min + n_digits_max) / 2;
+ js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
+ buf_tmp, sizeof(buf_tmp));
+- if (strtod(buf_tmp, NULL) == d) {
++ if (safe_strtod(buf_tmp, NULL) == d) {
+ /* no need to keep the trailing zeros */
+ while (n_digits >= 2 && buf[n_digits - 1] == '0')
+ n_digits--;
+@@ -11700,14 +11769,14 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
+ return JS_EXCEPTION;
+ }
+
+-static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
++static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
+ {
+ printf("%14s %4s %4s %14s %10s %s\n",
+ "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
+ }
+
+ /* for debug only: dump an object without side effect */
+-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
++static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+ {
+ uint32_t i;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+@@ -11784,7 +11853,7 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+ printf("[autoinit %p %d %p]",
+ (void *)js_autoinit_get_realm(pr),
+ js_autoinit_get_id(pr),
+- (void *)pr->u.init.opaque);
++ pr->u.init.opaque);
+ } else {
+ JS_DumpValueShort(rt, pr->u.value);
+ }
+@@ -11813,7 +11882,7 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+ printf("\n");
+ }
+
+-static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
++static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+ {
+ if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ JS_DumpObject(rt, (JSObject *)p);
+@@ -11845,7 +11914,7 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+ }
+ }
+
+-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
++static maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val)
+ {
+ uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
+@@ -11950,13 +12019,13 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ }
+ }
+
+-static __maybe_unused void JS_DumpValue(JSContext *ctx,
++static maybe_unused void JS_DumpValue(JSContext *ctx,
+ JSValueConst val)
+ {
+ JS_DumpValueShort(ctx->rt, val);
+ }
+
+-static __maybe_unused void JS_PrintValue(JSContext *ctx,
++static maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val)
+ {
+@@ -12439,7 +12508,7 @@ static JSObject *find_binary_op(JSBinaryOperatorDef *def,
+
+ /* return -1 if exception, 0 if no operator overloading, 1 if
+ overloaded operator called */
+-static __exception int js_call_binary_op_fallback(JSContext *ctx,
++static WARN_UNUSED int js_call_binary_op_fallback(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst op1,
+ JSValueConst op2,
+@@ -12568,7 +12637,7 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx,
+
+ /* try to call the operation on the operatorSet field of 'obj'. Only
+ used for "/" and "**" on the BigInt prototype in math mode */
+-static __exception int js_call_binary_op_simple(JSContext *ctx,
++static WARN_UNUSED int js_call_binary_op_simple(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst obj,
+ JSValueConst op1,
+@@ -12625,7 +12694,7 @@ static __exception int js_call_binary_op_simple(JSContext *ctx,
+
+ /* return -1 if exception, 0 if no operator overloading, 1 if
+ overloaded operator called */
+-static __exception int js_call_unary_op_fallback(JSContext *ctx,
++static WARN_UNUSED int js_call_unary_op_fallback(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst op1,
+ OPCodeEnum op)
+@@ -12835,7 +12904,7 @@ static int js_unary_arith_bigdecimal(JSContext *ctx,
+ return 0;
+ }
+
+-static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
++static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+ {
+@@ -12933,7 +13002,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
+ return -1;
+ }
+
+-static __exception int js_post_inc_slow(JSContext *ctx,
++static WARN_UNUSED int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+ {
+ JSValue op1;
+@@ -13288,7 +13357,7 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
+ return -1;
+ }
+
+-static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
++static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+ {
+ JSValue op1, op2, res;
+@@ -13452,7 +13521,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
+ return -1;
+ }
+
+-static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
++static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2, res;
+ uint32_t tag1, tag2;
+@@ -13566,7 +13635,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
+ return -1;
+ }
+
+-static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
++static no_inline WARN_UNUSED int js_binary_logic_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+ {
+@@ -13916,7 +13985,7 @@ static BOOL tag_is_number(uint32_t tag)
+ tag == JS_TAG_BIG_DECIMAL);
+ }
+
+-static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
++static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+ {
+ JSValue op1, op2, ret;
+@@ -14192,7 +14261,7 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+ return -1;
+ }
+
+-static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
++static no_inline warn_unused int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+ {
+@@ -14224,7 +14293,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
+ }
+
+ /* specific case necessary for correct return value semantics */
+-static __exception int js_post_inc_slow(JSContext *ctx,
++static warn_unused int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+ {
+ JSValue op1;
+@@ -14241,7 +14310,7 @@ static __exception int js_post_inc_slow(JSContext *ctx,
+ return 0;
+ }
+
+-static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
++static no_inline warn_unused int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+ {
+ JSValue op1, op2;
+@@ -14283,7 +14352,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
+ return -1;
+ }
+
+-static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
++static no_inline warn_unused int js_add_slow(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2;
+ uint32_t tag1, tag2;
+@@ -14331,7 +14400,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
+ return -1;
+ }
+
+-static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
++static no_inline warn_unused int js_binary_logic_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+ {
+@@ -14458,7 +14527,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
+ return -1;
+ }
+
+-static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
++static no_inline warn_unused int js_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+ {
+ JSValue op1, op2;
+@@ -14744,7 +14813,7 @@ static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
+ return 0;
+ }
+
+-static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
++static warn_unused int js_operator_in(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2;
+ JSAtom atom;
+@@ -14770,7 +14839,7 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
+ return 0;
+ }
+
+-static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
++static warn_unused int js_has_unscopable(JSContext *ctx, JSValueConst obj,
+ JSAtom atom)
+ {
+ JSValue arr, val;
+@@ -14788,7 +14857,7 @@ static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
+ return ret;
+ }
+
+-static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
++static warn_unused int js_operator_instanceof(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2;
+ BOOL ret;
+@@ -14804,7 +14873,7 @@ static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
+ return 0;
+ }
+
+-static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
++static warn_unused int js_operator_typeof(JSContext *ctx, JSValueConst op1)
+ {
+ JSAtom atom;
+ uint32_t tag;
+@@ -14861,7 +14930,7 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
+ return atom;
+ }
+
+-static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
++static warn_unused int js_operator_delete(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2;
+ JSAtom atom;
+@@ -15016,7 +15085,7 @@ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
+ var_ref = get_var_ref(ctx, sf, i, TRUE);
+ if (!var_ref)
+ goto fail;
+- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
++ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
+ if (!pr) {
+ free_var_ref(ctx->rt, var_ref);
+ goto fail;
+@@ -15188,7 +15257,7 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
+ }
+
+ /* obj -> enum_obj */
+-static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
++static warn_unused int js_for_in_start(JSContext *ctx, JSValue *sp)
+ {
+ sp[-1] = build_for_in_iterator(ctx, sp[-1]);
+ if (JS_IsException(sp[-1]))
+@@ -15197,7 +15266,7 @@ static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
+ }
+
+ /* enum_obj -> enum_obj value done */
+-static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
++static warn_unused int js_for_in_next(JSContext *ctx, JSValue *sp)
+ {
+ JSValueConst enum_obj;
+ JSObject *p;
+@@ -15218,7 +15287,7 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
+ if (it->is_array) {
+ if (it->idx >= it->array_length)
+ goto done;
+- prop = __JS_AtomFromUInt32(it->idx);
++ prop = JS_AtomFromUInt32(it->idx);
+ it->idx++;
+ } else {
+ JSShape *sh = p->shape;
+@@ -15411,7 +15480,7 @@ static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
+ }
+
+ /* obj -> enum_rec (3 slots) */
+-static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
++static warn_unused int js_for_of_start(JSContext *ctx, JSValue *sp,
+ BOOL is_async)
+ {
+ JSValue op1, obj, method;
+@@ -15432,7 +15501,7 @@ static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
+ objs. If 'done' is true or in case of exception, 'enum_rec' is set
+ to undefined. If 'done' is true, 'value' is always set to
+ undefined. */
+-static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
++static warn_unused int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
+ {
+ JSValue value = JS_UNDEFINED;
+ int done = 1;
+@@ -15478,7 +15547,7 @@ static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
+ return JS_EXCEPTION;
+ }
+
+-static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
++static warn_unused int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
+ {
+ JSValue obj, value;
+ BOOL done;
+@@ -15554,7 +15623,7 @@ static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
+ return FALSE;
+ }
+
+-static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
++static warn_unused int js_append_enumerate(JSContext *ctx, JSValue *sp)
+ {
+ JSValue iterator, enumobj, method, value;
+ int is_array_iterator;
+@@ -15634,7 +15703,7 @@ exception:
+ return -1;
+ }
+
+-static __exception int JS_CopyDataProperties(JSContext *ctx,
++static warn_unused int JS_CopyDataProperties(JSContext *ctx,
+ JSValueConst target,
+ JSValueConst source,
+ JSValueConst excluded,
+@@ -16043,7 +16112,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
+ #else
+ sf->js_mode = 0;
+ #endif
+- sf->cur_func = (JSValue)func_obj;
++ sf->cur_func = func_obj;
+ sf->arg_count = argc;
+ arg_buf = argv;
+
+@@ -16056,7 +16125,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
+ arg_buf[i] = JS_UNDEFINED;
+ sf->arg_count = arg_count;
+ }
+- sf->arg_buf = (JSValue*)arg_buf;
++ sf->arg_buf = arg_buf;
+
+ func = p->u.cfunc.c_function;
+ switch(cproto) {
+@@ -16226,7 +16295,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ #include "quickjs-opcode.h"
+ [ OP_COUNT ... 255 ] = &&case_default
+ };
+-#define SWITCH(pc) goto *dispatch_table[opcode = *pc++];
++#define SWITCH(pc) goto *dispatch_table[opcode = *(pc)++];
+ #define CASE(op) case_ ## op
+ #define DEFAULT case_default
+ #define BREAK SWITCH(pc)
+@@ -16269,7 +16338,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ return JS_ThrowTypeError(caller_ctx, "not a function");
+ }
+ return call_func(caller_ctx, func_obj, this_obj, argc,
+- (JSValueConst *)argv, flags);
++ argv, flags);
+ }
+ b = p->u.func.function_bytecode;
+
+@@ -16287,7 +16356,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ sf->js_mode = b->js_mode;
+ arg_buf = argv;
+ sf->arg_count = argc;
+- sf->cur_func = (JSValue)func_obj;
++ sf->cur_func = func_obj;
+ init_list_head(&sf->var_ref_list);
+ var_refs = p->u.func.var_refs;
+
+@@ -16315,6 +16384,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ rt->current_stack_frame = sf;
+ ctx = b->realm; /* set the current realm */
+
++ if (ctx->handleFunctionEntered)
++ ctx->handleFunctionEntered(ctx, this_obj);
++
+ restart:
+ for(;;) {
+ int call_argc;
+@@ -16420,12 +16492,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ int arg = *pc++;
+ switch(arg) {
+ case OP_SPECIAL_OBJECT_ARGUMENTS:
+- *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);
++ *sp++ = js_build_arguments(ctx, argc, argv);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
+- *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,
++ *sp++ = js_build_mapped_arguments(ctx, argc, argv,
+ sf, min_int(argc, b->arg_count));
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+@@ -16465,7 +16537,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ {
+ int first = get_u16(pc);
+ pc += 2;
+- *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);
++ *sp++ = js_build_rest(ctx, first, argc, argv);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ }
+@@ -16698,7 +16770,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ goto exception;
+ call_argv = sp - call_argc;
+ for(i = 0; i < call_argc; i++) {
+- ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],
++ ret = JS_DefinePropertyValue(ctx, ret_val, JS_AtomFromUInt32(i), call_argv[i],
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ call_argv[i] = JS_UNDEFINED;
+ if (ret < 0) {
+@@ -16717,7 +16789,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ magic = get_u16(pc);
+ pc += 2;
+
+- ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
++ ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-3]);
+@@ -16850,7 +16922,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ JS_EVAL_TYPE_DIRECT, scope_idx);
+ } else {
+ ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
+- (JSValueConst *)tab);
++ tab);
+ }
+ free_arg_list(ctx, tab, len);
+ if (unlikely(JS_IsException(ret_val)))
+@@ -17474,7 +17546,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ {
+ JSValue ret;
+ ret = JS_Call(ctx, sp[-3], sp[-4],
+- 1, (JSValueConst *)(sp - 1));
++ 1, (sp - 1));
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+@@ -17502,7 +17574,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ 0, NULL);
+ } else {
+ ret = JS_CallFree(ctx, method, sp[-4],
+- 1, (JSValueConst *)(sp - 1));
++ 1, (sp - 1));
+ }
+ if (JS_IsException(ret))
+ goto exception;
+@@ -17925,7 +17997,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+- sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) +
++ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) +
+ JS_VALUE_GET_FLOAT64(op2));
+ sp--;
+ } else {
+@@ -17990,7 +18062,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+- sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -
++ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) -
+ JS_VALUE_GET_FLOAT64(op2));
+ sp--;
+ } else {
+@@ -18033,7 +18105,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ #endif
+ d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
+ mul_fp_res:
+- sp[-2] = __JS_NewFloat64(ctx, d);
++ sp[-2] = JS_NewFloat64Impl(ctx, d);
+ sp--;
+ } else {
+ goto binary_arith_slow;
+@@ -18125,7 +18197,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ } else if (JS_TAG_IS_FLOAT64(tag)) {
+ d = -JS_VALUE_GET_FLOAT64(op1);
+ neg_fp_res:
+- sp[-1] = __JS_NewFloat64(ctx, d);
++ sp[-1] = JS_NewFloat64Impl(ctx, d);
+ } else {
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+@@ -18659,6 +18731,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ }
+ }
+ exception:
++ if (JS_IsString(rt->current_exception)) {
++ JSValue error_obj = JS_NewError(ctx);
++ JSAtom msgProp = JS_NewAtom(ctx, "message");
++ JS_DefinePropertyValue(ctx, error_obj, msgProp, rt->current_exception, 0);
++ rt->current_exception = error_obj;
++ JS_FreeAtom(ctx, msgProp);
++ }
+ if (is_backtrace_needed(ctx, rt->current_exception)) {
+ /* add the backtrace information now (it is not done
+ before if the exception happens in a bytecode
+@@ -18706,6 +18785,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ }
+ }
+ rt->current_stack_frame = sf->prev_frame;
++ if (ctx->handleFunctionExited)
++ ctx->handleFunctionExited(ctx);
+ return ret_val;
+ }
+
+@@ -18713,14 +18794,14 @@ JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+ {
+ return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
+- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
++ argc, argv, JS_CALL_FLAG_COPY_ARGV);
+ }
+
+ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+ {
+ JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
+- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
++ argc, argv, JS_CALL_FLAG_COPY_ARGV);
+ JS_FreeValue(ctx, func_obj);
+ return res;
+ }
+@@ -18825,7 +18906,7 @@ static JSValue JS_CallConstructorInternal(JSContext *ctx,
+ return JS_ThrowTypeError(ctx, "not a function");
+ }
+ return call_func(ctx, func_obj, new_target, argc,
+- (JSValueConst *)argv, flags);
++ argv, flags);
+ }
+
+ b = p->u.func.function_bytecode;
+@@ -18854,7 +18935,7 @@ JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
+ int argc, JSValueConst *argv)
+ {
+ return JS_CallConstructorInternal(ctx, func_obj, new_target,
+- argc, (JSValue *)argv,
++ argc, argv,
+ JS_CALL_FLAG_COPY_ARGV);
+ }
+
+@@ -18862,7 +18943,7 @@ JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
+ int argc, JSValueConst *argv)
+ {
+ return JS_CallConstructorInternal(ctx, func_obj, func_obj,
+- argc, (JSValue *)argv,
++ argc, argv,
+ JS_CALL_FLAG_COPY_ARGV);
+ }
+
+@@ -18885,7 +18966,7 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
+ }
+
+ /* JSAsyncFunctionState (used by generator and async functions) */
+-static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
++static warn_unused int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
+ JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+ {
+@@ -19227,7 +19308,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+ fail:
+ error = JS_GetException(ctx);
+ ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
+- 1, (JSValueConst *)&error);
++ 1, &error);
+ JS_FreeValue(ctx, error);
+ js_async_function_terminate(ctx->rt, s);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
+@@ -19238,7 +19319,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+ if (JS_IsUndefined(func_ret)) {
+ /* function returned */
+ ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
+- 1, (JSValueConst *)&value);
++ 1, &value);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, value);
+ js_async_function_terminate(ctx->rt, s);
+@@ -19249,7 +19330,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+ /* await */
+ JS_FreeValue(ctx, func_ret); /* not used */
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+- 1, (JSValueConst *)&value, 0);
++ 1, &value, 0);
+ JS_FreeValue(ctx, value);
+ if (JS_IsException(promise))
+ goto fail;
+@@ -19531,7 +19612,7 @@ static int js_async_generator_completed_return(JSContext *ctx,
+ int res;
+
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+- 1, (JSValueConst *)&value, 0);
++ 1, &value, 0);
+ if (JS_IsException(promise))
+ return -1;
+ if (js_async_generator_resolve_function_create(ctx,
+@@ -19703,7 +19784,7 @@ static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
+ JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
+ err = JS_GetException(ctx);
+ res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+- 1, (JSValueConst *)&err);
++ 1, &err);
+ JS_FreeValue(ctx, err);
+ JS_FreeValue(ctx, res2);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+@@ -20138,7 +20219,7 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
+ #define short_opcode_info(op) opcode_info[op]
+ #endif
+
+-static __exception int next_token(JSParseState *s);
++static warn_unused int next_token(JSParseState *s);
+
+ static void free_token(JSParseState *s, JSToken *token)
+ {
+@@ -20169,7 +20250,7 @@ static void free_token(JSParseState *s, JSToken *token)
+ }
+ }
+
+-static void __attribute((unused)) dump_token(JSParseState *s,
++static void maybe_unused dump_token(JSParseState *s,
+ const JSToken *token)
+ {
+ switch(token->val) {
+@@ -20230,7 +20311,7 @@ static void __attribute((unused)) dump_token(JSParseState *s,
+ }
+ }
+
+-int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)
++int FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, const char *fmt, ...)
+ {
+ JSContext *ctx = s->ctx;
+ va_list ap;
+@@ -20276,7 +20357,7 @@ static int js_parse_error_reserved_identifier(JSParseState *s)
+ s->token.u.ident.atom));
+ }
+
+-static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
++static warn_unused int js_parse_template_part(JSParseState *s, const uint8_t *p)
+ {
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+@@ -20337,7 +20418,7 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
+ return -1;
+ }
+
+-static __exception int js_parse_string(JSParseState *s, int sep,
++static warn_unused int js_parse_string(JSParseState *s, int sep,
+ BOOL do_throw, const uint8_t *p,
+ JSToken *token, const uint8_t **pp)
+ {
+@@ -20482,7 +20563,7 @@ static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
+ !s->token.u.ident.has_escape;
+ }
+
+-static __exception int js_parse_regexp(JSParseState *s)
++static warn_unused int js_parse_regexp(JSParseState *s)
+ {
+ const uint8_t *p;
+ BOOL in_class;
+@@ -20580,8 +20661,8 @@ static __exception int js_parse_regexp(JSParseState *s)
+ return -1;
+ }
+
+-static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
+- char *static_buf)
++static warn_unused int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
++ const char *static_buf)
+ {
+ char *buf, *new_buf;
+ size_t size, new_size;
+@@ -20656,7 +20737,7 @@ static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
+ }
+
+
+-static __exception int next_token(JSParseState *s)
++static warn_unused int next_token(JSParseState *s)
+ {
+ const uint8_t *p;
+ int c;
+@@ -21204,7 +21285,7 @@ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
+ return atom;
+ }
+
+-static __exception int json_next_token(JSParseState *s)
++static warn_unused int json_next_token(JSParseState *s)
+ {
+ const uint8_t *p;
+ int c;
+@@ -21644,7 +21725,7 @@ static int cpool_add(JSParseState *s, JSValue val)
+ return fd->cpool_count - 1;
+ }
+
+-static __exception int emit_push_const(JSParseState *s, JSValueConst val,
++static warn_unused int emit_push_const(JSParseState *s, JSValueConst val,
+ BOOL as_atom)
+ {
+ int idx;
+@@ -21654,7 +21735,7 @@ static __exception int emit_push_const(JSParseState *s, JSValueConst val,
+ /* warning: JS_NewAtomStr frees the string value */
+ JS_DupValue(s->ctx, val);
+ atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
+- if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {
++ if (atom != JS_ATOM_NULL && !JS_AtomIsTaggedInt(atom)) {
+ emit_op(s, OP_push_atom_value);
+ emit_u32(s, atom);
+ return 0;
+@@ -22118,14 +22199,14 @@ static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
+ return idx;
+ }
+
+-static __exception int js_parse_expr(JSParseState *s);
+-static __exception int js_parse_function_decl(JSParseState *s,
++static warn_unused int js_parse_expr(JSParseState *s);
++static warn_unused int js_parse_function_decl(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name, const uint8_t *ptr,
+ int start_line);
+ static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
+-static __exception int js_parse_function_decl2(JSParseState *s,
++static warn_unused int js_parse_function_decl2(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+@@ -22133,9 +22214,9 @@ static __exception int js_parse_function_decl2(JSParseState *s,
+ int function_line_num,
+ JSParseExportEnum export_flag,
+ JSFunctionDef **pfd);
+-static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);
+-static __exception int js_parse_assign_expr(JSParseState *s);
+-static __exception int js_parse_unary(JSParseState *s, int parse_flags);
++static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags);
++static warn_unused int js_parse_assign_expr(JSParseState *s);
++static warn_unused int js_parse_unary(JSParseState *s, int parse_flags);
+ static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
+ JSAtom label_name,
+ int label_break, int label_cont,
+@@ -22162,7 +22243,7 @@ static int seal_template_obj(JSContext *ctx, JSValueConst obj)
+ return 0;
+ }
+
+-static __exception int js_parse_template(JSParseState *s, int call, int *argc)
++static warn_unused int js_parse_template(JSParseState *s, int call, int *argc)
+ {
+ JSContext *ctx = s->ctx;
+ JSValue raw_array, template_object;
+@@ -22293,7 +22374,7 @@ static BOOL token_is_ident(int tok)
+ }
+
+ /* if the property is an expression, name = JS_ATOM_NULL */
+-static int __exception js_parse_property_name(JSParseState *s,
++static int warn_unused js_parse_property_name(JSParseState *s,
+ JSAtom *pname,
+ BOOL allow_method, BOOL allow_var,
+ BOOL allow_private)
+@@ -22435,7 +22516,7 @@ static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
+ return 0;
+ }
+
+-static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
++static warn_unused int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
+ {
+ s->token.line_num = sp->last_line_num;
+ s->line_num = sp->line_num;
+@@ -22480,7 +22561,8 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_
+ size_t level = 0;
+ JSParsePos pos;
+ int last_tok, tok = TOK_EOF;
+- int c, tok_len, bits = 0;
++ int tok_len, bits = 0;
++ char c;
+
+ /* protect from underflow */
+ state[level++] = 0;
+@@ -22640,7 +22722,7 @@ static void set_object_name_computed(JSParseState *s)
+ }
+ }
+
+-static __exception int js_parse_object_literal(JSParseState *s)
++static warn_unused int js_parse_object_literal(JSParseState *s)
+ {
+ JSAtom name = JS_ATOM_NULL;
+ const uint8_t *start_ptr;
+@@ -22765,15 +22847,15 @@ static __exception int js_parse_object_literal(JSParseState *s)
+ /* forbid the exponentiation operator in js_parse_unary() */
+ #define PF_POW_FORBIDDEN (1 << 4)
+
+-static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);
++static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags);
+
+-static __exception int js_parse_left_hand_side_expr(JSParseState *s)
++static warn_unused int js_parse_left_hand_side_expr(JSParseState *s)
+ {
+ return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
+ }
+
+ /* XXX: could generate specific bytecode */
+-static __exception int js_parse_class_default_ctor(JSParseState *s,
++static warn_unused int js_parse_class_default_ctor(JSParseState *s,
+ BOOL has_super,
+ JSFunctionDef **pfd)
+ {
+@@ -22864,7 +22946,7 @@ typedef struct {
+ int brand_push_pos;
+ } ClassFieldsDef;
+
+-static __exception int emit_class_init_start(JSParseState *s,
++static warn_unused int emit_class_init_start(JSParseState *s,
+ ClassFieldsDef *cf)
+ {
+ int label_add_brand;
+@@ -22897,7 +22979,7 @@ static __exception int emit_class_init_start(JSParseState *s,
+ return 0;
+ }
+
+-static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)
++static warn_unused int add_brand(JSParseState *s, ClassFieldsDef *cf)
+ {
+ if (!cf->has_brand) {
+ /* define the brand field in 'this' of the initializer */
+@@ -22929,7 +23011,7 @@ static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
+ }
+
+
+-static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
++static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr,
+ JSParseExportEnum export_flag)
+ {
+ JSContext *ctx = s->ctx;
+@@ -23374,7 +23456,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
+ return -1;
+ }
+
+-static __exception int js_parse_array_literal(JSParseState *s)
++static warn_unused int js_parse_array_literal(JSParseState *s)
+ {
+ uint32_t idx;
+ BOOL need_length;
+@@ -23410,7 +23492,7 @@ static __exception int js_parse_array_literal(JSParseState *s)
+ if (js_parse_assign_expr(s))
+ return -1;
+ emit_op(s, OP_define_field);
+- emit_u32(s, __JS_AtomFromUInt32(idx));
++ emit_u32(s, JS_AtomFromUInt32(idx));
+ need_length = FALSE;
+ }
+ idx++;
+@@ -23520,7 +23602,7 @@ static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
+ return FALSE;
+ }
+
+-static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
++static warn_unused int get_lvalue(JSParseState *s, int *popcode, int *pscope,
+ JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
+ int tok)
+ {
+@@ -23760,7 +23842,7 @@ static void put_lvalue(JSParseState *s, int opcode, int scope,
+ }
+ }
+
+-static __exception int js_parse_expr_paren(JSParseState *s)
++static warn_unused int js_parse_expr_paren(JSParseState *s)
+ {
+ if (js_parse_expect(s, '('))
+ return -1;
+@@ -23778,7 +23860,7 @@ static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
+ JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
+ }
+
+-static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)
++static warn_unused int js_define_var(JSParseState *s, JSAtom name, int tok)
+ {
+ JSFunctionDef *fd = s->cur_func;
+ JSVarDefEnum var_def_type;
+@@ -24327,7 +24409,7 @@ static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
+ }
+
+ /* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */
+-static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags)
+ {
+ FuncCallType call_type;
+ int optional_chaining_label;
+@@ -24963,17 +25045,16 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
+ return 0;
+ }
+
+-static __exception int js_parse_delete(JSParseState *s)
++static warn_unused int js_parse_delete(JSParseState *s)
+ {
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom name;
+- int opcode;
+
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+- switch(opcode = get_prev_opcode(fd)) {
++ switch (get_prev_opcode(fd)) {
+ case OP_get_field:
+ {
+ JSValue val;
+@@ -25024,7 +25105,7 @@ static __exception int js_parse_delete(JSParseState *s)
+ }
+
+ /* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */
+-static __exception int js_parse_unary(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_unary(JSParseState *s, int parse_flags)
+ {
+ int op;
+
+@@ -25172,7 +25253,7 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags)
+ }
+
+ /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+-static __exception int js_parse_expr_binary(JSParseState *s, int level,
++static warn_unused int js_parse_expr_binary(JSParseState *s, int level,
+ int parse_flags)
+ {
+ int op, opcode;
+@@ -25319,7 +25400,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level,
+ }
+
+ /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+-static __exception int js_parse_logical_and_or(JSParseState *s, int op,
++static warn_unused int js_parse_logical_and_or(JSParseState *s, int op,
+ int parse_flags)
+ {
+ int label1;
+@@ -25361,7 +25442,7 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op,
+ return 0;
+ }
+
+-static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
+ {
+ int label1;
+
+@@ -25389,7 +25470,7 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
+ }
+
+ /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+-static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_cond_expr(JSParseState *s, int parse_flags)
+ {
+ int label1, label2;
+
+@@ -25420,7 +25501,7 @@ static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
+ static void emit_return(JSParseState *s, BOOL hasval);
+
+ /* allowed parse_flags: PF_IN_ACCEPTED */
+-static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags)
+ {
+ int opcode, op, scope;
+ JSAtom name0 = JS_ATOM_NULL;
+@@ -25666,13 +25747,13 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
+ return 0;
+ }
+
+-static __exception int js_parse_assign_expr(JSParseState *s)
++static warn_unused int js_parse_assign_expr(JSParseState *s)
+ {
+ return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
+ }
+
+ /* allowed parse_flags: PF_IN_ACCEPTED */
+-static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_expr2(JSParseState *s, int parse_flags)
+ {
+ BOOL comma = FALSE;
+ for(;;) {
+@@ -25696,7 +25777,7 @@ static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
+ return 0;
+ }
+
+-static __exception int js_parse_expr(JSParseState *s)
++static warn_unused int js_parse_expr(JSParseState *s)
+ {
+ return js_parse_expr2(s, PF_IN_ACCEPTED);
+ }
+@@ -25724,7 +25805,7 @@ static void pop_break_entry(JSFunctionDef *fd)
+ fd->top_break = be->prev;
+ }
+
+-static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
++static warn_unused int emit_break(JSParseState *s, JSAtom name, int is_cont)
+ {
+ BlockEnv *top;
+ int i, scope_level;
+@@ -25873,15 +25954,15 @@ static void emit_return(JSParseState *s, BOOL hasval)
+ #define DECL_MASK_OTHER (1 << 2) /* all other declarations */
+ #define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
+
+-static __exception int js_parse_statement_or_decl(JSParseState *s,
++static warn_unused int js_parse_statement_or_decl(JSParseState *s,
+ int decl_mask);
+
+-static __exception int js_parse_statement(JSParseState *s)
++static warn_unused int js_parse_statement(JSParseState *s)
+ {
+ return js_parse_statement_or_decl(s, 0);
+ }
+
+-static __exception int js_parse_block(JSParseState *s)
++static warn_unused int js_parse_block(JSParseState *s)
+ {
+ if (js_parse_expect(s, '{'))
+ return -1;
+@@ -25901,7 +25982,7 @@ static __exception int js_parse_block(JSParseState *s)
+ }
+
+ /* allowed parse_flags: PF_IN_ACCEPTED */
+-static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
++static warn_unused int js_parse_var(JSParseState *s, int parse_flags, int tok,
+ BOOL export_flag)
+ {
+ JSContext *ctx = s->ctx;
+@@ -26052,7 +26133,7 @@ static int is_let(JSParseState *s, int decl_mask)
+
+ /* XXX: handle IteratorClose when exiting the loop before the
+ enumeration is done */
+-static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
++static warn_unused int js_parse_for_in_of(JSParseState *s, int label_name,
+ BOOL is_async)
+ {
+ JSContext *ctx = s->ctx;
+@@ -26283,7 +26364,7 @@ static void set_eval_ret_undefined(JSParseState *s)
+ }
+ }
+
+-static __exception int js_parse_statement_or_decl(JSParseState *s,
++static warn_unused int js_parse_statement_or_decl(JSParseState *s,
+ int decl_mask)
+ {
+ JSContext *ctx = s->ctx;
+@@ -27603,7 +27684,7 @@ static int find_exported_name(GetExportNamesState *s, JSAtom name)
+ return -1;
+ }
+
+-static __exception int get_exported_names(JSContext *ctx,
++static warn_unused int get_exported_names(JSContext *ctx,
+ GetExportNamesState *s,
+ JSModuleDef *m, BOOL from_star)
+ {
+@@ -27744,7 +27825,6 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
+ en->u.var_ref = res_me->u.local.var_ref;
+ } else {
+ JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
+- p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
+ en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
+ }
+ }
+@@ -28237,7 +28317,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
+ goto exception;
+
+ ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
+- 1, (JSValueConst *)&ns);
++ 1, &ns);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, ns);
+ JS_FreeCString(ctx, basename);
+@@ -28246,7 +28326,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
+
+ err = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+- 1, (JSValueConst *)&err);
++ 1, &err);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, err);
+ JS_FreeCString(ctx, basename);
+@@ -28343,7 +28423,7 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
+ return ret_val;
+ }
+
+-static __exception JSAtom js_parse_from_clause(JSParseState *s)
++static warn_unused JSAtom js_parse_from_clause(JSParseState *s)
+ {
+ JSAtom module_name;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
+@@ -28366,7 +28446,7 @@ static __exception JSAtom js_parse_from_clause(JSParseState *s)
+ return module_name;
+ }
+
+-static __exception int js_parse_export(JSParseState *s)
++static warn_unused int js_parse_export(JSParseState *s)
+ {
+ JSContext *ctx = s->ctx;
+ JSModuleDef *m = s->cur_func->module;
+@@ -28569,7 +28649,7 @@ static int add_import(JSParseState *s, JSModuleDef *m,
+ return 0;
+ }
+
+-static __exception int js_parse_import(JSParseState *s)
++static warn_unused int js_parse_import(JSParseState *s)
+ {
+ JSContext *ctx = s->ctx;
+ JSModuleDef *m = s->cur_func->module;
+@@ -28684,7 +28764,7 @@ static __exception int js_parse_import(JSParseState *s)
+ return js_parse_expect_semi(s);
+ }
+
+-static __exception int js_parse_source_element(JSParseState *s)
++static warn_unused int js_parse_source_element(JSParseState *s)
+ {
+ JSFunctionDef *fd = s->cur_func;
+ int tok;
+@@ -29608,8 +29688,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
+ }
+ var_idx = idx;
+ break;
+- } else
+- if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
++ } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
+@@ -30359,7 +30438,7 @@ static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
+
+ /* for direct eval compilation: add references to the variables of the
+ calling function */
+-static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
++static warn_unused int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionBytecode *b, int scope_idx)
+ {
+ int i, count;
+@@ -30779,7 +30858,7 @@ static int get_label_pos(JSFunctionDef *s, int label)
+
+ /* convert global variable accesses to local variables or closure
+ variables when necessary */
+-static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
++static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s)
+ {
+ int pos, pos_next, bc_len, op, len, i, idx, line_num;
+ uint8_t *bc_buf;
+@@ -31343,7 +31422,7 @@ static void put_short_code(DynBuf *bc_out, int op, int idx)
+ }
+
+ /* peephole optimizations and resolve goto/labels */
+-static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
++static warn_unused int resolve_labels(JSContext *ctx, JSFunctionDef *s)
+ {
+ int pos, pos_next, bc_len, op, op1, len, i, line_num;
+ const uint8_t *bc_buf;
+@@ -32152,8 +32231,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
+ bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
+ }
+ goto shrink;
+- } else
+- if (diff == (int16_t)diff && op == OP_goto) {
++ } else if (diff == (int16_t)diff && op == OP_goto) {
+ //put_u16(bc_out.buf + pos, diff);
+ jp->size = 2;
+ delta = 2;
+@@ -32236,7 +32314,7 @@ typedef struct StackSizeState {
+ } StackSizeState;
+
+ /* 'op' is only used for error indication */
+-static __exception int ss_check(JSContext *ctx, StackSizeState *s,
++static warn_unused int ss_check(JSContext *ctx, StackSizeState *s,
+ int pos, int op, int stack_len)
+ {
+ if ((unsigned)pos >= s->bc_len) {
+@@ -32272,7 +32350,7 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s,
+ return 0;
+ }
+
+-static __exception int compute_stack_size(JSContext *ctx,
++static warn_unused int compute_stack_size(JSContext *ctx,
+ JSFunctionDef *fd,
+ int *pstack_size)
+ {
+@@ -32593,7 +32671,9 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
+ }
+ } else {
+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
++ if (fd->arg_count)
+ memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
++ if (fd->var_count)
+ memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
+ }
+ b->var_count = fd->var_count;
+@@ -32721,7 +32801,7 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
+ }
+ }
+
+-static __exception int js_parse_directives(JSParseState *s)
++static warn_unused int js_parse_directives(JSParseState *s)
+ {
+ char str[20];
+ JSParsePos pos;
+@@ -32895,7 +32975,7 @@ static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
+
+ /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
+ JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */
+-static __exception int js_parse_function_decl2(JSParseState *s,
++static warn_unused int js_parse_function_decl2(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+@@ -33443,7 +33523,7 @@ done:
+ return -1;
+ }
+
+-static __exception int js_parse_function_decl(JSParseState *s,
++static warn_unused int js_parse_function_decl(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+@@ -33455,7 +33535,7 @@ static __exception int js_parse_function_decl(JSParseState *s,
+ NULL);
+ }
+
+-static __exception int js_parse_program(JSParseState *s)
++static warn_unused int js_parse_program(JSParseState *s)
+ {
+ JSFunctionDef *fd = s->cur_func;
+ int idx;
+@@ -33497,12 +33577,12 @@ static __exception int js_parse_program(JSParseState *s)
+
+ static void js_parse_init(JSContext *ctx, JSParseState *s,
+ const char *input, size_t input_len,
+- const char *filename)
++ const char *filename, int line)
+ {
+ memset(s, 0, sizeof(*s));
+ s->ctx = ctx;
+ s->filename = filename;
+- s->line_num = 1;
++ s->line_num = line;
+ s->buf_ptr = (const uint8_t *)input;
+ s->buf_end = s->buf_ptr + input_len;
+ s->token.val = ' ';
+@@ -33573,9 +33653,9 @@ static void skip_shebang(JSParseState *s)
+ }
+
+ /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
+-static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
++static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int flags, int scope_idx)
++ const char *filename, int line, int flags, int scope_idx)
+ {
+ JSParseState s1, *s = &s1;
+ int err, js_mode, eval_type;
+@@ -33586,7 +33666,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ JSFunctionDef *fd;
+ JSModuleDef *m;
+
+- js_parse_init(ctx, s, input, input_len, filename);
++ js_parse_init(ctx, s, input, input_len, filename, line);
+ skip_shebang(s);
+
+ eval_type = flags & JS_EVAL_TYPE_MASK;
+@@ -33620,7 +33700,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ js_mode |= JS_MODE_STRICT;
+ }
+ }
+- fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);
++ fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, line);
+ if (!fd)
+ goto fail1;
+ s->cur_func = fd;
+@@ -33686,12 +33766,12 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ /* the indirection is needed to make 'eval' optional */
+ static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int flags, int scope_idx)
++ const char *filename, int line, int flags, int scope_idx)
+ {
+ if (unlikely(!ctx->eval_internal)) {
+ return JS_ThrowTypeError(ctx, "eval is not supported");
+ }
+- return ctx->eval_internal(ctx, this_obj, input, input_len, filename,
++ return ctx->eval_internal(ctx, this_obj, input, input_len, filename, line,
+ flags, scope_idx);
+ }
+
+@@ -33707,7 +33787,7 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+ str = JS_ToCStringLen(ctx, &len, val);
+ if (!str)
+ return JS_EXCEPTION;
+- ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);
++ ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", 1, flags, scope_idx);
+ JS_FreeCString(ctx, str);
+ return ret;
+
+@@ -33715,14 +33795,14 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+
+ JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int eval_flags)
++ const char *filename, int line, int eval_flags)
+ {
+ int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
+ JSValue ret;
+
+ assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
+ eval_type == JS_EVAL_TYPE_MODULE);
+- ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename,
++ ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, line,
+ eval_flags, -1);
+ return ret;
+ }
+@@ -33730,7 +33810,7 @@ JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
+ const char *filename, int eval_flags)
+ {
+- return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename,
++ return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, 1,
+ eval_flags);
+ }
+
+@@ -33948,7 +34028,7 @@ static void bc_put_u16(BCWriterState *s, uint16_t v)
+ dbuf_put_u16(&s->dbuf, v);
+ }
+
+-static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
++static maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
+ {
+ if (s->byte_swap)
+ v = bswap32(v);
+@@ -33982,7 +34062,7 @@ static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
+ {
+ uint32_t v;
+
+- if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {
++ if (atom < s->first_atom || JS_AtomIsTaggedInt(atom)) {
+ *pres = atom;
+ return 0;
+ }
+@@ -34022,8 +34102,8 @@ static int bc_put_atom(BCWriterState *s, JSAtom atom)
+ {
+ uint32_t v;
+
+- if (__JS_AtomIsTaggedInt(atom)) {
+- v = (__JS_AtomToUInt32(atom) << 1) | 1;
++ if (JS_AtomIsTaggedInt(atom)) {
++ v = (JS_AtomToUInt32(atom) << 1) | 1;
+ } else {
+ if (bc_atom_to_idx(s, &v, atom))
+ return -1;
+@@ -34142,7 +34222,7 @@ static int JS_WriteFunctionBytecode(BCWriterState *s,
+ static void JS_WriteString(BCWriterState *s, JSString *p)
+ {
+ int i;
+- bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);
++ bc_put_leb128(s, (p->len << 1) | p->is_wide_char);
+ if (p->is_wide_char) {
+ for(i = 0; i < p->len; i++)
+ bc_put_u16(s, p->u.str16[i]);
+@@ -34800,7 +34880,7 @@ typedef struct BCReaderState {
+ } BCReaderState;
+
+ #ifdef DUMP_READ_OBJECT
+-static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
++static void FORMAT_ATTR(2, 3) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
+ va_list ap;
+ int i, n, n0;
+
+@@ -34863,7 +34943,7 @@ static int bc_get_u16(BCReaderState *s, uint16_t *pval)
+ return 0;
+ }
+
+-static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
++static maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
+ {
+ if (unlikely(s->buf_end - s->ptr < 4)) {
+ *pval = 0; /* avoid warning */
+@@ -34937,7 +35017,7 @@ static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
+ {
+ JSAtom atom;
+
+- if (__JS_AtomIsTaggedInt(idx)) {
++ if (JS_AtomIsTaggedInt(idx)) {
+ atom = idx;
+ } else if (idx < s->first_atom) {
+ atom = JS_DupAtom(s->ctx, idx);
+@@ -34961,7 +35041,7 @@ static int bc_get_atom(BCReaderState *s, JSAtom *patom)
+ if (bc_get_leb128(s, &v))
+ return -1;
+ if (v & 1) {
+- *patom = __JS_AtomFromUInt32(v >> 1);
++ *patom = JS_AtomFromUInt32(v >> 1);
+ return 0;
+ } else {
+ return bc_idx_to_atom(s, patom, v >> 1);
+@@ -35776,7 +35856,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s)
+ if (bc_get_u64(s, &u.u64))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%g\n", u.d);
+- obj = __JS_NewFloat64(ctx, u.d);
++ obj = JS_NewFloat64Impl(ctx, u.d);
+ }
+ break;
+ case BC_TAG_STRING:
+@@ -36084,7 +36164,7 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
+ val = JS_NewInt64(ctx, e->u.i64);
+ break;
+ case JS_DEF_PROP_DOUBLE:
+- val = __JS_NewFloat64(ctx, e->u.f64);
++ val = JS_NewFloat64Impl(ctx, e->u.f64);
+ break;
+ case JS_DEF_PROP_UNDEFINED:
+ val = JS_UNDEFINED;
+@@ -36148,7 +36228,7 @@ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
+ val = JS_NewInt64(ctx, e->u.i64);
+ break;
+ case JS_DEF_PROP_DOUBLE:
+- val = __JS_NewFloat64(ctx, e->u.f64);
++ val = JS_NewFloat64Impl(ctx, e->u.f64);
+ break;
+ case JS_DEF_OBJECT:
+ val = JS_NewObject(ctx);
+@@ -36381,7 +36461,7 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
+ return -1;
+ }
+
+-static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
++static warn_unused int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst desc,
+ int flags)
+ {
+@@ -36397,7 +36477,7 @@ static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
+ return ret;
+ }
+
+-static __exception int JS_ObjectDefineProperties(JSContext *ctx,
++static warn_unused int JS_ObjectDefineProperties(JSContext *ctx,
+ JSValueConst obj,
+ JSValueConst properties)
+ {
+@@ -36882,32 +36962,6 @@ static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
+ return JS_NewBool(ctx, ret);
+ }
+
+-static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
+- int argc, JSValueConst *argv)
+-{
+- JSValue obj;
+- JSAtom atom;
+- JSObject *p;
+- BOOL ret;
+-
+- obj = JS_ToObject(ctx, argv[0]);
+- if (JS_IsException(obj))
+- return obj;
+- atom = JS_ValueToAtom(ctx, argv[1]);
+- if (unlikely(atom == JS_ATOM_NULL)) {
+- JS_FreeValue(ctx, obj);
+- return JS_EXCEPTION;
+- }
+- p = JS_VALUE_GET_OBJ(obj);
+- ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
+- JS_FreeAtom(ctx, atom);
+- JS_FreeValue(ctx, obj);
+- if (ret < 0)
+- return JS_EXCEPTION;
+- else
+- return JS_NewBool(ctx, ret);
+-}
+-
+ static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+@@ -37447,6 +37501,32 @@ exception:
+ return res;
+ }
+
++static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
++ int argc, JSValueConst *argv)
++{
++ JSValue obj;
++ JSAtom atom;
++ JSObject *p;
++ BOOL ret;
++
++ obj = JS_ToObject(ctx, argv[0]);
++ if (JS_IsException(obj))
++ return obj;
++ atom = JS_ValueToAtom(ctx, argv[1]);
++ if (unlikely(atom == JS_ATOM_NULL)) {
++ JS_FreeValue(ctx, obj);
++ return JS_EXCEPTION;
++ }
++ p = JS_VALUE_GET_OBJ(obj);
++ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
++ JS_FreeAtom(ctx, atom);
++ JS_FreeValue(ctx, obj);
++ if (ret < 0)
++ return JS_EXCEPTION;
++ else
++ return JS_NewBool(ctx, ret);
++}
++
+ static const JSCFunctionListEntry js_object_funcs[] = {
+ JS_CFUNC_DEF("create", 2, js_object_create ),
+ JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
+@@ -37576,7 +37656,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
+ return JS_EXCEPTION;
+ }
+
+-static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
++static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres,
+ JSValueConst obj)
+ {
+ JSValue len_val;
+@@ -37588,7 +37668,7 @@ static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
+ return JS_ToUint32Free(ctx, pres, len_val);
+ }
+
+-static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
++static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres,
+ JSValueConst obj)
+ {
+ JSValue len_val;
+@@ -37673,9 +37753,9 @@ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
+ if (!tab)
+ return JS_EXCEPTION;
+ if (magic & 1) {
+- ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);
++ ret = JS_CallConstructor2(ctx, this_val, this_arg, len, tab);
+ } else {
+- ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);
++ ret = JS_Call(ctx, this_val, this_arg, len, tab);
+ }
+ free_arg_list(ctx, tab, len);
+ return ret;
+@@ -38889,8 +38969,7 @@ static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
+ method = JS_GetProperty(ctx, obj, JS_ATOM_join);
+ if (JS_IsException(method)) {
+ ret = JS_EXCEPTION;
+- } else
+- if (!JS_IsFunction(ctx, method)) {
++ } else if (!JS_IsFunction(ctx, method)) {
+ /* Use intrinsic Object.prototype.toString */
+ JS_FreeValue(ctx, method);
+ ret = js_object_toString(ctx, obj, 0, NULL);
+@@ -39138,8 +39217,7 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
+ if (argc == 0) {
+ item_count = 0;
+ del_count = 0;
+- } else
+- if (argc == 1) {
++ } else if (argc == 1) {
+ item_count = 0;
+ del_count = len - start;
+ } else {
+@@ -39283,8 +39361,8 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
+ if (!JS_IsUndefined(mapperFunction)) {
+ JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
+ element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
+- JS_FreeValue(ctx, (JSValue)args[0]);
+- JS_FreeValue(ctx, (JSValue)args[1]);
++ JS_FreeValue(ctx, args[0]);
++ JS_FreeValue(ctx, args[1]);
+ if (JS_IsException(element))
+ return -1;
+ }
+@@ -39915,7 +39993,7 @@ static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
+ if (f < 0 || f > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+ if (fabs(d) >= 1e21) {
+- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
++ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ } else {
+ return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);
+ }
+@@ -39936,7 +40014,7 @@ static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
+ if (JS_ToInt32Sat(ctx, &f, argv[0]))
+ return JS_EXCEPTION;
+ if (!isfinite(d)) {
+- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
++ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ }
+ if (JS_IsUndefined(argv[0])) {
+ flags = 0;
+@@ -39968,7 +40046,7 @@ static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
+ return JS_EXCEPTION;
+ if (!isfinite(d)) {
+ to_string:
+- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
++ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ }
+ if (p < 1 || p > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+@@ -40089,11 +40167,11 @@ static int js_string_get_own_property(JSContext *ctx,
+ uint32_t idx, ch;
+
+ /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
+- if (__JS_AtomIsTaggedInt(prop)) {
++ if (JS_AtomIsTaggedInt(prop)) {
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
+ p1 = JS_VALUE_GET_STRING(p->u.object_data);
+- idx = __JS_AtomToUInt32(prop);
++ idx = JS_AtomToUInt32(prop);
+ if (idx < p1->len) {
+ if (desc) {
+ if (p1->is_wide_char)
+@@ -40122,8 +40200,8 @@ static int js_string_define_own_property(JSContext *ctx,
+ JSObject *p;
+ JSString *p1, *p2;
+
+- if (__JS_AtomIsTaggedInt(prop)) {
+- idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ idx = JS_AtomToUInt32(prop);
+ p = JS_VALUE_GET_OBJ(this_obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
+ goto def;
+@@ -40157,8 +40235,8 @@ static int js_string_delete_property(JSContext *ctx,
+ {
+ uint32_t idx;
+
+- if (__JS_AtomIsTaggedInt(prop)) {
+- idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ idx = JS_AtomToUInt32(prop);
+ if (idx < js_string_obj_get_length(ctx, obj)) {
+ return FALSE;
+ }
+@@ -40701,7 +40779,7 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
+ str = JS_NewString(ctx, "g");
+ if (JS_IsException(str))
+ goto fail;
+- args[args_len++] = (JSValueConst)str;
++ args[args_len++] = str;
+ }
+ rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
+ JS_FreeValue(ctx, str);
+@@ -40710,7 +40788,7 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
+ JS_FreeValue(ctx, S);
+ return JS_EXCEPTION;
+ }
+- result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);
++ result = JS_InvokeFree(ctx, rx, atom, 1, &S);
+ JS_FreeValue(ctx, S);
+ return result;
+ }
+@@ -41759,7 +41837,7 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
+ uint32_t tag;
+
+ if (unlikely(argc == 0)) {
+- return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
++ return JS_NewFloat64Impl(ctx, is_max ? INFINITY : -INFINITY);
+ }
+
+ tag = JS_VALUE_GET_TAG(argv[0]);
+@@ -41911,9 +41989,13 @@ static uint64_t xorshift64star(uint64_t *pstate)
+
+ static void js_random_init(JSContext *ctx)
+ {
++#ifdef _MSC_VER
++ ctx->random_state = time(NULL);
++#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
++#endif
+ /* the state must be non zero */
+ if (ctx->random_state == 0)
+ ctx->random_state = 1;
+@@ -41928,15 +42010,21 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
+ v = xorshift64star(&ctx->random_state);
+ /* 1.0 <= u.d < 2 */
+ u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
+- return __JS_NewFloat64(ctx, u.d - 1.0);
++ return JS_NewFloat64Impl(ctx, u.d - 1.0);
+ }
+
++// MSVC inexplicably refuses to initialize the array below with
++// these functions, so use wrappers.
++static double floorWrapper(double x) { return floor(x); }
++static double ceilWrapper(double x) { return ceil(x); }
++static double log2Wrapper(double x) { return log2(x); }
++
+ static const JSCFunctionListEntry js_math_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
+ JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
+ JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
+- JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
+- JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
++ JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floorWrapper ),
++ JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceilWrapper ),
+ JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
+ JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
+
+@@ -41961,7 +42049,7 @@ static const JSCFunctionListEntry js_math_funcs[] = {
+ JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
+ JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
+ JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
+- JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),
++ JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2Wrapper ),
+ JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
+ JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
+ JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
+@@ -42004,9 +42092,16 @@ static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ int64_t d;
++#ifdef _MSC_VER
++ SYSTEMTIME st;
++ GetSystemTime(&st);
++ SystemTimeToFileTime(&st, (FILETIME *) &d);
++ d /= 10;
++#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
++#endif
+ return JS_NewInt64(ctx, d);
+ }
+
+@@ -43686,7 +43781,7 @@ JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
+ JSParseState s1, *s = &s1;
+ JSValue val = JS_UNDEFINED;
+
+- js_parse_init(ctx, s, buf, buf_len, filename);
++ js_parse_init(ctx, s, buf, buf_len, filename, 1);
+ s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
+ if (json_next_token(s))
+ goto fail;
+@@ -43937,7 +44032,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
+ goto exception;
+ }
+ #endif
+- v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
++ v = js_array_includes(ctx, jsc->stack, 1, &val);
+ if (JS_IsException(v))
+ goto exception;
+ if (JS_ToBoolFree(ctx, v)) {
+@@ -43958,7 +44053,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
+ sep = JS_DupValue(ctx, jsc->empty);
+ sep1 = JS_DupValue(ctx, jsc->empty);
+ }
+- v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);
++ v = js_array_push(ctx, jsc->stack, 1, &val, 0);
+ if (check_exception_free(ctx, v))
+ goto exception;
+ ret = JS_IsArray(ctx, val);
+@@ -43998,7 +44093,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
+ if (!JS_IsUndefined(jsc->property_list))
+ tab = JS_DupValue(ctx, jsc->property_list);
+ else
+- tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);
++ tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY);
+ if (JS_IsException(tab))
+ goto exception;
+ if (js_get_length64(ctx, &len, tab))
+@@ -44146,7 +44241,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
+ continue;
+ }
+ present = js_array_includes(ctx, jsc->property_list,
+- 1, (JSValueConst *)&v);
++ 1, &v);
+ if (JS_IsException(present)) {
+ JS_FreeValue(ctx, v);
+ goto exception;
+@@ -44270,7 +44365,7 @@ static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
+ tab = build_arg_list(ctx, &len, array_arg);
+ if (!tab)
+ return JS_EXCEPTION;
+- ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);
++ ret = JS_CallConstructor2(ctx, func, new_target, len, tab);
+ free_arg_list(ctx, tab, len);
+ return ret;
+ }
+@@ -44475,7 +44570,7 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
+ return JS_EXCEPTION;
+ if (JS_IsUndefined(method))
+ return JS_GetPrototype(ctx, s->target);
+- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
++ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return ret;
+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
+@@ -44562,7 +44657,7 @@ static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_IsExtensible(ctx, s->target);
+- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
++ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+@@ -44588,7 +44683,7 @@ static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_PreventExtensions(ctx, s->target);
+- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
++ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+@@ -45072,7 +45167,7 @@ static int js_proxy_get_own_property_names(JSContext *ctx,
+ JS_VALUE_GET_OBJ(s->target),
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
+ }
+- prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
++ prop_array = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(prop_array))
+ return -1;
+ tab = NULL;
+@@ -45408,7 +45503,7 @@ static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
+ if (JS_IsException(val))
+ return val;
+ /* XXX: use JS_ToStringInternal() with a flags */
+- ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);
++ ret = js_string_constructor(ctx, JS_UNDEFINED, 1, &val);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+@@ -45558,7 +45653,7 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
+ break;
+ }
+ if (is_set) {
+- ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
++ ret = JS_Call(ctx, adder, obj, 1, &item);
+ if (JS_IsException(ret)) {
+ JS_FreeValue(ctx, item);
+ goto fail;
+@@ -45729,7 +45824,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
+ } else {
+ JS_DupValue(ctx, key);
+ }
+- mr->key = (JSValue)key;
++ mr->key = key;
+ h = map_hash_key(ctx, key) & (s->hash_size - 1);
+ list_add_tail(&mr->hash_link, &s->hash_table[h]);
+ list_add_tail(&mr->link, &s->records);
+@@ -45951,7 +46046,7 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
+ args[0] = args[1];
+ else
+ args[0] = JS_DupValue(ctx, mr->value);
+- args[2] = (JSValue)this_val;
++ args[2] = this_val;
+ ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
+ JS_FreeValue(ctx, args[0]);
+ if (!magic)
+@@ -46343,7 +46438,7 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc,
+ functions */
+ if (!JS_IsUndefined(func)) {
+ res2 = JS_Call(ctx, func, JS_UNDEFINED,
+- 1, (JSValueConst *)&res);
++ 1, &res);
+ } else {
+ res2 = JS_UNDEFINED;
+ }
+@@ -46426,7 +46521,7 @@ static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
+ res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
+ if (JS_IsException(res)) {
+ JSValue error = JS_GetException(ctx);
+- res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
++ res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ }
+ JS_FreeValue(ctx, args[0]);
+@@ -46626,7 +46721,7 @@ static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
+ if (JS_IsException(ret)) {
+ JSValue ret2, error;
+ error = JS_GetException(ctx);
+- ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
++ ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret2))
+ goto fail1;
+@@ -46683,10 +46778,10 @@ static JSValue js_new_promise_capability(JSContext *ctx,
+
+ if (JS_IsUndefined(ctor)) {
+ result_promise = js_promise_constructor(ctx, ctor, 1,
+- (JSValueConst *)&executor);
++ &executor);
+ } else {
+ result_promise = JS_CallConstructor(ctx, ctor, 1,
+- (JSValueConst *)&executor);
++ &executor);
+ }
+ if (JS_IsException(result_promise))
+ goto fail;
+@@ -46770,7 +46865,7 @@ static JSValue js_promise___newPromiseCapability(JSContext *ctx,
+ }
+ #endif
+
+-static __exception int remainingElementsCount_add(JSContext *ctx,
++static warn_unused int remainingElementsCount_add(JSContext *ctx,
+ JSValueConst resolve_element_env,
+ int addend)
+ {
+@@ -46851,10 +46946,10 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx,
+ error = js_aggregate_error_constructor(ctx, values);
+ if (JS_IsException(error))
+ return JS_EXCEPTION;
+- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);
++ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ } else {
+- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);
++ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &values);
+ }
+ if (JS_IsException(ret))
+ return ret;
+@@ -46890,7 +46985,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ fail_reject:
+ error = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
+- (JSValueConst *)&error);
++ &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret))
+ goto fail;
+@@ -46921,7 +47016,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ if (done)
+ break;
+ next_promise = JS_Call(ctx, promise_resolve,
+- this_val, 1, (JSValueConst *)&item);
++ this_val, 1, &item);
+ JS_FreeValue(ctx, item);
+ if (JS_IsException(next_promise)) {
+ fail_reject1:
+@@ -46929,7 +47024,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ goto fail_reject;
+ }
+ resolve_element_data[0] = JS_NewBool(ctx, FALSE);
+- resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);
++ resolve_element_data[1] = JS_NewInt32(ctx, index);
+ resolve_element_data[2] = values;
+ resolve_element_data[3] = resolving_funcs[is_promise_any];
+ resolve_element_data[4] = resolve_element_env;
+@@ -46989,7 +47084,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ values = error;
+ }
+ ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
+- 1, (JSValueConst *)&values);
++ 1, &values);
+ if (check_exception_free(ctx, ret))
+ goto fail_reject;
+ }
+@@ -47032,7 +47127,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
+ fail_reject:
+ error = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
+- (JSValueConst *)&error);
++ &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret))
+ goto fail;
+@@ -47051,7 +47146,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
+ if (done)
+ break;
+ next_promise = JS_Call(ctx, promise_resolve,
+- this_val, 1, (JSValueConst *)&item);
++ this_val, 1, &item);
+ JS_FreeValue(ctx, item);
+ if (JS_IsException(next_promise)) {
+ fail_reject1:
+@@ -47078,7 +47173,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
+ goto done;
+ }
+
+-static __exception int perform_promise_then(JSContext *ctx,
++static warn_unused int perform_promise_then(JSContext *ctx,
+ JSValueConst promise,
+ JSValueConst *resolve_reject,
+ JSValueConst *cap_resolving_funcs)
+@@ -47196,7 +47291,7 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va
+ res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
+ if (JS_IsException(res))
+ return res;
+- promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);
++ promise = js_promise_resolve(ctx, ctor, 1, &res, 0);
+ JS_FreeValue(ctx, res);
+ if (JS_IsException(promise))
+ return promise;
+@@ -47211,7 +47306,7 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va
+ JS_FreeValue(ctx, promise);
+ return then_func;
+ }
+- ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);
++ ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, &then_func);
+ JS_FreeValue(ctx, then_func);
+ return ret;
+ }
+@@ -47288,7 +47383,7 @@ static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
+ {
+ JSValueConst func_data[1];
+
+- func_data[0] = (JSValueConst)JS_NewBool(ctx, done);
++ func_data[0] = JS_NewBool(ctx, done);
+ return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
+ 1, 0, 1, func_data);
+ }
+@@ -47411,7 +47506,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi
+ is_reject = 1;
+ done_resolve:
+ res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
+- 1, (JSValueConst *)&err);
++ 1, &err);
+ JS_FreeValue(ctx, err);
+ JS_FreeValue(ctx, res2);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+@@ -47423,7 +47518,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi
+ int res;
+
+ value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
+- 1, (JSValueConst *)&value, 0);
++ 1, &value, 0);
+ if (JS_IsException(value_wrapper_promise)) {
+ JS_FreeValue(ctx, value);
+ goto reject;
+@@ -47593,7 +47688,7 @@ static int isURIReserved(int c) {
+ return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
+ }
+
+-static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
++static int FORMAT_ATTR(2, 3) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
+ {
+ va_list ap;
+
+@@ -47894,7 +47989,7 @@ static int64_t floor_div(int64_t a, int64_t b) {
+ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+-static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
++static warn_unused int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
+ {
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+@@ -47954,7 +48049,7 @@ static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+ static char const day_names[] = "SunMonTueWedThuFriSat";
+
+-static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
++static warn_unused int get_date_fields(JSContext *ctx, JSValueConst obj,
+ double fields[9], int is_local, int force)
+ {
+ double dval;
+@@ -48016,7 +48111,7 @@ static double time_clip(double t) {
+
+ /* The spec mandates the use of 'double' and it fixes the order
+ of the operations */
+-static double set_date_fields(double fields[], int is_local) {
++static double set_date_fields(const double fields[], int is_local) {
+ int64_t y;
+ double days, d, h, m1;
+ int i, m, md;
+@@ -48213,9 +48308,17 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
+
+ /* OS dependent: return the UTC time in ms since 1970. */
+ static int64_t date_now(void) {
++#ifdef _MSC_VER
++ SYSTEMTIME st;
++ GetSystemTime(&st);
++ int64_t d;
++ SystemTimeToFileTime(&st, (FILETIME *) &d);
++ return d /= 10000;
++#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
++#endif
+ }
+
+ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
+@@ -48246,7 +48349,7 @@ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
+ }
+ v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
+ if (JS_IsString(v)) {
+- dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
++ dv = js_Date_parse(ctx, JS_UNDEFINED, 1, &v);
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(dv))
+ return JS_EXCEPTION;
+@@ -48814,7 +48917,7 @@ void JS_AddIntrinsicDate(JSContext *ctx)
+
+ void JS_AddIntrinsicEval(JSContext *ctx)
+ {
+- ctx->eval_internal = __JS_EvalInternal;
++ ctx->eval_internal = JS_EvalInternalImpl;
+ }
+
+ #ifdef CONFIG_BIGNUM
+@@ -50934,7 +51037,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+- JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));
++ JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, &ctx->throw_type_error, 1));
+
+ ctx->global_obj = JS_NewObject(ctx);
+ ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);
+@@ -52090,7 +52193,7 @@ exception:
+
+ #define special_indexOf 0
+ #define special_lastIndexOf 1
+-#define special_includes -1
++#define special_includes (-1)
+
+ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int special)
+@@ -52157,8 +52260,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
+ is_int = 1;
+ v64 = JS_VALUE_GET_INT(argv[0]);
+ d = v64;
+- } else
+- if (tag == JS_TAG_FLOAT64) {
++ } else if (tag == JS_TAG_FLOAT64) {
+ d = JS_VALUE_GET_FLOAT64(argv[0]);
+ v64 = d;
+ is_int = (v64 == d);
+@@ -52659,11 +52761,11 @@ static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
+ #endif
+
+ static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
+- return __JS_NewFloat64(ctx, *(const float *)a);
++ return JS_NewFloat64Impl(ctx, *(const float *)a);
+ }
+
+ static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
+- return __JS_NewFloat64(ctx, *(const double *)a);
++ return JS_NewFloat64Impl(ctx, *(const double *)a);
+ }
+
+ struct TA_sort_context {
+@@ -52717,8 +52819,8 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
+ psc->exception = 1;
+ }
+ done:
+- JS_FreeValue(ctx, (JSValue)argv[0]);
+- JS_FreeValue(ctx, (JSValue)argv[1]);
++ JS_FreeValue(ctx, argv[0]);
++ JS_FreeValue(ctx, argv[1]);
+ }
+ return cmp;
+ }
+@@ -53285,7 +53387,7 @@ static JSValue js_dataview_getValue(JSContext *ctx,
+ case JS_CLASS_INT8_ARRAY:
+ return JS_NewInt32(ctx, *(int8_t *)ptr);
+ case JS_CLASS_UINT8_ARRAY:
+- return JS_NewInt32(ctx, *(uint8_t *)ptr);
++ return JS_NewInt32(ctx, *ptr);
+ case JS_CLASS_INT16_ARRAY:
+ v = get_u16(ptr);
+ if (is_swap)
+@@ -53336,7 +53438,7 @@ static JSValue js_dataview_getValue(JSContext *ctx,
+ if (is_swap)
+ v = bswap32(v);
+ u.i = v;
+- return __JS_NewFloat64(ctx, u.f);
++ return JS_NewFloat64Impl(ctx, u.f);
+ }
+ case JS_CLASS_FLOAT64_ARRAY:
+ {
+@@ -53347,7 +53449,7 @@ static JSValue js_dataview_getValue(JSContext *ctx,
+ u.i = get_u64(ptr);
+ if (is_swap)
+ u.i = bswap64(u.i);
+- return __JS_NewFloat64(ctx, u.f);
++ return JS_NewFloat64Impl(ctx, u.f);
+ }
+ default:
+ abort();
+@@ -54059,3 +54161,155 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx)
+ JS_AddIntrinsicAtomics(ctx);
+ #endif
+ }
++
++#ifndef NDEBUG
++static void *watchedRefCount = NULL;
++
++void notifyRefCountIncrease(JSRefCountHeader *p)
++{
++ if (p == watchedRefCount)
++ fprintf(stderr, "increasing ref count %d for %p\n", p->ref_count, watchedRefCount);
++}
++
++void notifyRefCountDecrease(JSRefCountHeader *p)
++{
++ if (p == watchedRefCount)
++ fprintf(stderr, "decreasing ref count %d for %p\n", p->ref_count, watchedRefCount);
++}
++
++void watchRefCount(void *p)
++{
++ watchedRefCount = p;
++}
++#endif
++
++void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup)
++{
++ ctx->scopeLookup = scopeLookup;
++}
++
++void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler)
++{
++ ctx->handleUndefined = handler;
++}
++
++void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler)
++{
++ ctx->handleFunctionEntered = handler;
++}
++
++void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler)
++{
++ ctx->handleFunctionExited = handler;
++}
++
++int isSimpleValue(JSValue v)
++{
++ JSObject *p;
++ if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
++ return 1;
++ p = JS_VALUE_GET_OBJ(v);
++ return p->class_id >= JS_CLASS_OBJECT && p->class_id <= JS_CLASS_BOOLEAN;
++}
++
++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);
++}
++
++JSValue mkVal(int32_t tag, int32_t val)
++{
++ return (JSValue){ (JSValueUnion){ .int32 = val }, tag };
++}
++
++JSValue mkPtr(int32_t tag, void *p)
++{
++ return (JSValue){ (JSValueUnion){ .ptr = p }, tag };
++}
++
++void JS_FreeValue(JSContext *ctx, JSValue v) {
++ if (JS_VALUE_HAS_REF_COUNT(v)) {
++ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
++#ifndef NDEBUG
++ notifyRefCountDecrease(p);
++#endif
++ if (--p->ref_count <= 0) {
++ JS_FreeValueImpl(ctx, v);
++ }
++ }
++}
++void JS_FreeValueRT(JSRuntime *rt, JSValue v) {
++ if (JS_VALUE_HAS_REF_COUNT(v)) {
++ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
++#ifndef NDEBUG
++ notifyRefCountDecrease(p);
++#endif
++ if (--p->ref_count <= 0) {
++ JS_FreeValueRTImpl(rt, v);
++ }
++ }
++}
++JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) {
++ (void)ctx;
++ return JS_MKVAL(JS_TAG_BOOL, (val != 0));
++}
++JSValue JS_NewInt32(JSContext *ctx, int32_t val) {
++ (void)ctx;
++ return JS_MKVAL(JS_TAG_INT, val);
++}
++JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) {
++ (void)ctx;
++ return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);
++}
++JSValue JS_NewInt64(JSContext *ctx, int64_t val) {
++ JSValue v;
++ if (val == (int32_t)val) {
++ v = JS_NewInt32(ctx, (int32_t)val);
++ } else {
++ v = JS_NewFloat64Impl(ctx, (double)val);
++ }
++ return v;
++}
++JSValue JS_NewUint32(JSContext *ctx, uint32_t val) {
++ JSValue v;
++ if (val <= 0x7fffffff) {
++ v = JS_NewInt32(ctx, val);
++ } else {
++ v = JS_NewFloat64Impl(ctx, val);
++ }
++ return v;
++}
++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_NewFloat64Impl(ctx, d);
++ }
++ return v;
++}
++JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
++ int length) {
++ return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0);
++}
++
++JS_BOOL JS_IsRegExp(JSContext *ctx, JSValue val)
++{
++ JSObject *p;
++ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
++ return FALSE;
++ return JS_VALUE_GET_OBJ(val)->class_id == JS_CLASS_REGEXP;
++}
+diff --git a/quickjs.h b/quickjs.h
+index d4a5cd3..a5adf0c 100644
+--- a/quickjs.h
++++ b/quickjs.h
+@@ -215,15 +215,19 @@ typedef struct JSValue {
+ #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
+ #define JS_VALUE_GET_PTR(v) ((v).u.ptr)
+
+-#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
+-#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
++JSValue mkVal(int32_t tag, int32_t val);
++JSValue mkPtr(int32_t tag, void *p);
++
++#define JS_MKVAL(tag, val) mkVal(tag, val)
++#define JS_MKPTR(tag, p) mkPtr(tag, p)
+
+ #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)
++static inline JSValue JS_NewFloat64Impl(JSContext *ctx, double d)
+ {
++ (void) ctx;
+ JSValue v;
+ v.tag = JS_TAG_FLOAT64;
+ v.u.float64 = d;
+@@ -502,66 +506,14 @@ 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 != 0));
+-}
+-
+-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);
+-}
+-
+-static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val)
+-{
+- JSValue v;
+- if (val == (int32_t)val) {
+- v = JS_NewInt32(ctx, val);
+- } else {
+- v = __JS_NewFloat64(ctx, val);
+- }
+- return v;
+-}
+-
+-static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)
+-{
+- JSValue v;
+- if (val <= 0x7fffffff) {
+- v = JS_NewInt32(ctx, val);
+- } else {
+- v = __JS_NewFloat64(ctx, val);
+- }
+- return v;
+-}
+-
++JSValue JS_NewBool(JSContext *ctx, JS_BOOL val);
++JSValue JS_NewInt32(JSContext *ctx, int32_t val);
++JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val);
++JSValue JS_NewInt64(JSContext *ctx, int64_t val);
++JSValue JS_NewUint32(JSContext *ctx, uint32_t val);
+ JSValue JS_NewBigInt64(JSContext *ctx, int64_t v);
+ JSValue JS_NewBigUint64(JSContext *ctx, uint64_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;
+-}
++JSValue JS_NewFloat64(JSContext *ctx, double d);
+
+ static inline JS_BOOL JS_IsNumber(JSValueConst v)
+ {
+@@ -571,6 +523,7 @@ static inline JS_BOOL JS_IsNumber(JSValueConst v)
+
+ static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
+ {
++ (void) ctx;
+ int tag = JS_VALUE_GET_TAG(v);
+ return tag == JS_TAG_BIG_INT;
+ }
+@@ -639,43 +592,38 @@ JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fm
+ 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);
+- }
+- }
+-}
++#ifndef NDEBUG
++void notifyRefCountIncrease(JSRefCountHeader *p);
++void notifyRefCountDecrease(JSRefCountHeader *p);
++#endif
++
++void JS_FreeValue(JSContext *ctx, JSValue v);
++void JS_FreeValueRT(JSRuntime *rt, JSValue v);
+
+ static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
+ {
++ (void) ctx;
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
++#ifndef NDEBUG
++ notifyRefCountIncrease(p);
++#endif
+ p->ref_count++;
+ }
+- return (JSValue)v;
++ return v;
+ }
+
+ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
+ {
++ (void) rt;
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
++#ifndef NDEBUG
++ notifyRefCountIncrease(p);
++#endif
+ p->ref_count++;
+ }
+- return (JSValue)v;
++ return v;
+ }
+
+ int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
+@@ -714,6 +662,7 @@ JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);
+ JSValue JS_NewObject(JSContext *ctx);
+
+ JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val);
++JS_BOOL JS_IsRegExp(JSContext* ctx, JSValueConst val);
+ JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val);
+ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val);
+
+@@ -783,7 +732,7 @@ JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
+ /* same as JS_Eval() but with an explicit 'this_obj' parameter */
+ JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int eval_flags);
++ const char *filename, int line, int eval_flags);
+ JSValue JS_GetGlobalObject(JSContext *ctx);
+ int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj);
+ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+@@ -945,18 +894,11 @@ 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);
+-}
++JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
++ int length);
+
+-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);
+-}
++JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
++ const char *name, int length, JSCFunctionEnum cproto, int magic);
+ void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst proto);
+
+@@ -1039,6 +981,35 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
+ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
+ const JSCFunctionListEntry *tab, int len);
+
++
++/* Qbs extensions */
++struct LookupResult
++{
++ JSValue value;
++ JSValue scope;
++ int useResult;
++};
++typedef struct LookupResult ScopeLookup(JSContext *ctx, JSAtom prop);
++void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup);
++
++// Alternative: Request with throw in script engine
++typedef void FoundUndefinedHandler(JSContext *ctx);
++void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler);
++
++typedef void FunctionEnteredHandler(JSContext *ctx, JSValue this_val);
++typedef void FunctionExitedHandler(JSContext *ctx);
++void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler);
++void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler);
++int isSimpleValue(JSValue v);
++
++#ifndef NDEBUG
++void watchRefCount(void *p);
++#endif
++
++void build_backtrace(JSContext *ctx, JSValueConst error_obj,
++ const char *filename, int line_num,
++ int backtrace_flags);
++
+ #undef js_unlikely
+ #undef js_force_inline
+
diff --git a/src/shared/quickjs/quickjs.h b/src/shared/quickjs/quickjs.h
new file mode 100644
index 000000000..a5adf0cd2
--- /dev/null
+++ b/src/shared/quickjs/quickjs.h
@@ -0,0 +1,1020 @@
+/*
+ * QuickJS Javascript Engine
+ *
+ * Copyright (c) 2017-2021 Fabrice Bellard
+ * Copyright (c) 2017-2021 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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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 INTPTR_MAX >= INT64_MAX
+#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 = -11, /* first negative tag */
+ JS_TAG_BIG_DECIMAL = -11,
+ JS_TAG_BIG_INT = -10,
+ JS_TAG_BIG_FLOAT = -9,
+ JS_TAG_SYMBOL = -8,
+ JS_TAG_STRING = -7,
+ 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);
+}
+
+static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
+{
+ return 0;
+}
+
+#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;
+}
+
+static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
+{
+ uint32_t tag;
+ tag = JS_VALUE_GET_TAG(v);
+ return tag == (JS_NAN >> 32);
+}
+
+#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)
+
+JSValue mkVal(int32_t tag, int32_t val);
+JSValue mkPtr(int32_t tag, void *p);
+
+#define JS_MKVAL(tag, val) mkVal(tag, val)
+#define JS_MKPTR(tag, p) mkPtr(tag, p)
+
+#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_NewFloat64Impl(JSContext *ctx, double d)
+{
+ (void) ctx;
+ JSValue v;
+ v.tag = JS_TAG_FLOAT64;
+ v.u.float64 = d;
+ return v;
+}
+
+static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
+{
+ union {
+ double d;
+ uint64_t u64;
+ } u;
+ if (v.tag != JS_TAG_FLOAT64)
+ return 0;
+ u.d = v.u.float64;
+ return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000;
+}
+
+#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_STRICT (1 << 3) /* force 'strict' mode */
+#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */
+/* compile but do not run. The result is an object with a
+ JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
+ with JS_EvalFunction(). */
+#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
+/* don't include the stack frames before this eval in the Error() backtraces */
+#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
+
+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;
+
+typedef struct JSGCObjectHeader JSGCObjectHeader;
+
+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);
+/* use 0 to disable maximum stack size check */
+void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size);
+/* should be called when changing thread to update the stack top value
+ used to check stack overflow. */
+void JS_UpdateStackTop(JSRuntime *rt);
+JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque);
+void JS_FreeRuntime(JSRuntime *rt);
+void *JS_GetRuntimeOpaque(JSRuntime *rt);
+void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque);
+typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp);
+void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
+void JS_RunGC(JSRuntime *rt);
+JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj);
+
+JSContext *JS_NewContext(JSRuntime *rt);
+void JS_FreeContext(JSContext *s);
+JSContext *JS_DupContext(JSContext *ctx);
+void *JS_GetContextOpaque(JSContext *ctx);
+void JS_SetContextOpaque(JSContext *ctx, void *opaque);
+JSRuntime *JS_GetRuntime(JSContext *ctx);
+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);
+void JS_AddIntrinsicBigInt(JSContext *ctx);
+void JS_AddIntrinsicBigFloat(JSContext *ctx);
+void JS_AddIntrinsicBigDecimal(JSContext *ctx);
+/* enable operator overloading */
+void JS_AddIntrinsicOperators(JSContext *ctx);
+/* enable "use math" */
+void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable);
+
+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 */
+#define JS_ATOM_NULL 0
+
+JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t 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);
+JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val);
+
+/* 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);
+#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0)
+typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_val, int argc, JSValueConst *argv,
+ int flags);
+
+typedef struct JSClassDef {
+ const char *class_name;
+ JSClassFinalizer *finalizer;
+ JSClassGCMark *gc_mark;
+ /* if call != NULL, the object is a function. If (flags &
+ JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a
+ constructor. In this case, 'this_val' is new.target. A
+ constructor call only happens if the object constructor bit is
+ set (see JS_SetConstructorBit()). */
+ 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 */
+
+JSValue JS_NewBool(JSContext *ctx, JS_BOOL val);
+JSValue JS_NewInt32(JSContext *ctx, int32_t val);
+JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val);
+JSValue JS_NewInt64(JSContext *ctx, int64_t val);
+JSValue JS_NewUint32(JSContext *ctx, uint32_t val);
+JSValue JS_NewBigInt64(JSContext *ctx, int64_t v);
+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
+JSValue JS_NewFloat64(JSContext *ctx, double d);
+
+static inline JS_BOOL JS_IsNumber(JSValueConst v)
+{
+ int tag = JS_VALUE_GET_TAG(v);
+ return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag);
+}
+
+static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
+{
+ (void) ctx;
+ int tag = JS_VALUE_GET_TAG(v);
+ return 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_IsBigDecimal(JSValueConst v)
+{
+ int tag = JS_VALUE_GET_TAG(v);
+ return tag == JS_TAG_BIG_DECIMAL;
+}
+
+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_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);
+
+#ifndef NDEBUG
+void notifyRefCountIncrease(JSRefCountHeader *p);
+void notifyRefCountDecrease(JSRefCountHeader *p);
+#endif
+
+void JS_FreeValue(JSContext *ctx, JSValue v);
+void JS_FreeValueRT(JSRuntime *rt, JSValue v);
+
+static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
+{
+ (void) ctx;
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+#ifndef NDEBUG
+ notifyRefCountIncrease(p);
+#endif
+ p->ref_count++;
+ }
+ return v;
+}
+
+static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
+{
+ (void) rt;
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+#ifndef NDEBUG
+ notifyRefCountIncrease(p);
+#endif
+ p->ref_count++;
+ }
+ return v;
+}
+
+int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
+int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
+static inline int 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);
+/* return an exception if 'val' is a Number */
+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
+/* same as JS_ToInt64() but allow BigInt */
+int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val);
+
+JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t 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_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8);
+static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1)
+{
+ return JS_ToCStringLen2(ctx, plen, val1, 0);
+}
+static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1)
+{
+ return JS_ToCStringLen2(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_IsRegExp(JSContext* ctx, JSValueConst val);
+JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val);
+JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL 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);
+JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val);
+
+#define JS_GPN_STRING_MASK (1 << 0)
+#define JS_GPN_SYMBOL_MASK (1 << 1)
+#define JS_GPN_PRIVATE_MASK (1 << 2)
+/* only include the enumerable properties */
+#define JS_GPN_ENUM_ONLY (1 << 4)
+/* set theJSPropertyEnum.is_enumerable field */
+#define JS_GPN_SET_ENUM (1 << 5)
+
+int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
+ uint32_t *plen, JSValueConst obj, int flags);
+int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop);
+
+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);
+JS_BOOL JS_DetectModule(const char *input, size_t input_len);
+/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
+JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
+ const char *filename, int eval_flags);
+/* same as JS_Eval() but with an explicit 'this_obj' parameter */
+JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int eval_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);
+
+/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
+JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename);
+#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */
+JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename, int flags);
+JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
+ JSValueConst replacer, JSValueConst space0);
+
+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);
+JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
+ size_t *pbyte_offset,
+ size_t *pbyte_length,
+ size_t *pbytes_per_element);
+typedef struct {
+ void *(*sab_alloc)(void *opaque, size_t size);
+ void (*sab_free)(void *opaque, void *ptr);
+ void (*sab_dup)(void *opaque, void *ptr);
+ void *sab_opaque;
+} JSSharedArrayBufferFunctions;
+void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
+ const JSSharedArrayBufferFunctions *sf);
+
+JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
+
+/* is_handled = TRUE means that the rejection is handled */
+typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,
+ JSValueConst reason,
+ JS_BOOL is_handled, void *opaque);
+void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque);
+
+/* 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);
+/* set the [IsHTMLDDA] internal slot */
+void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj);
+
+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);
+/* return the import.meta object of a module */
+JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);
+JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);
+
+/* 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 */
+#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
+#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to
+ encode arbitrary object
+ graph */
+uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags);
+uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags, uint8_t ***psab_tab, size_t *psab_tab_len);
+
+#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */
+#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */
+#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
+#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */
+JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
+ int flags);
+/* instantiate and evaluate a bytecode function. Only used when
+ reading a script or module with JS_ReadObject() */
+JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj);
+/* load the dependencies of the module 'obj'. Useful when JS_ReadObject()
+ returns a module. */
+int JS_ResolveModule(JSContext *ctx, JSValueConst obj);
+
+/* only exported for os.Worker() */
+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);
+/* only exported for os.Worker() */
+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
+ const char *filename);
+
+/* 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);
+
+JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
+ int length);
+
+JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
+ const char *name, int length, JSCFunctionEnum cproto, int magic);
+void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst proto);
+
+/* 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
+
+/* Note: c++ does not like nested designators */
+#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 }, .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 }, .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);
+
+
+/* Qbs extensions */
+struct LookupResult
+{
+ JSValue value;
+ JSValue scope;
+ int useResult;
+};
+typedef struct LookupResult ScopeLookup(JSContext *ctx, JSAtom prop);
+void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup);
+
+// Alternative: Request with throw in script engine
+typedef void FoundUndefinedHandler(JSContext *ctx);
+void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler);
+
+typedef void FunctionEnteredHandler(JSContext *ctx, JSValue this_val);
+typedef void FunctionExitedHandler(JSContext *ctx);
+void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler);
+void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler);
+int isSimpleValue(JSValue v);
+
+#ifndef NDEBUG
+void watchRefCount(void *p);
+#endif
+
+void build_backtrace(JSContext *ctx, JSValueConst error_obj,
+ const char *filename, int line_num,
+ int backtrace_flags);
+
+#undef js_unlikely
+#undef js_force_inline
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+#endif /* QUICKJS_H */
diff --git a/src/shared/quickjs/quickjs.qbs b/src/shared/quickjs/quickjs.qbs
new file mode 100644
index 000000000..4c8112ac9
--- /dev/null
+++ b/src/shared/quickjs/quickjs.qbs
@@ -0,0 +1,33 @@
+StaticLibrary {
+ name: "quickjs"
+
+ Depends { name: "qbsbuildconfig" }
+ Depends { name: "cpp" }
+
+ cpp.cLanguageVersion: qbs.toolchain.contains("msvc") ? "c99" : "gnu99"
+ cpp.defines: ['CONFIG_VERSION="2021-03-27"'].concat(qbsbuildconfig.dumpJsLeaks
+ ? "DUMP_LEAKS" : [])
+ cpp.warningLevel: "none"
+
+ files: [
+ "cutils.c",
+ "cutils.h",
+ "libregexp-opcode.h",
+ "libregexp.c",
+ "libregexp.h",
+ "libunicode-table.h",
+ "libunicode.c",
+ "libunicode.h",
+ "list.h",
+ "quickjs-atom.h",
+ "quickjs-opcode.h",
+ "quickjs.c",
+ "quickjs.diff",
+ "quickjs.h",
+ ]
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+}
diff --git a/src/src.qbs b/src/src.qbs
index 39cb18730..84c045f43 100644
--- a/src/src.qbs
+++ b/src/src.qbs
@@ -5,8 +5,9 @@ Project {
"libexec/libexec.qbs",
"packages/packages.qbs",
"plugins/plugins.qbs",
- "shared/json/json.qbs",
"shared/bundledqt/bundledqt.qbs",
+ "shared/json/json.qbs",
+ "shared/quickjs/quickjs.qbs",
"shared/variant/variant.qbs",
]
}
diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp
index 0c1b44fad..28bbe4638 100644
--- a/tests/auto/api/tst_api.cpp
+++ b/tests/auto/api/tst_api.cpp
@@ -1010,7 +1010,7 @@ void TestApi::errorInSetupRunEnvironment()
qbs::ErrorInfo error;
const QProcessEnvironment env = runEnv.runEnvironment(&error);
QVERIFY(error.hasError());
- QVERIFY(error.toString().contains("trallala"));
+ QVERIFY2(error.toString().contains("trallala"), qPrintable(error.toString()));
} catch (const qbs::ErrorInfo &) {
exceptionCaught = true;
}
@@ -1434,7 +1434,7 @@ void TestApi::infiniteLoopResolving()
m_logSink, nullptr));
QTimer::singleShot(1000, setupJob.get(), &qbs::AbstractJob::cancel);
QVERIFY(waitForFinished(setupJob.get(), testTimeoutInMsecs()));
- QVERIFY2(setupJob->error().toString().toLower().contains("cancel"),
+ QVERIFY2(setupJob->error().toString().toLower().contains("interrupted"),
qPrintable(setupJob->error().toString()));
}
diff --git a/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs b/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs
deleted file mode 100644
index 802aa1450..000000000
--- a/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs
+++ /dev/null
@@ -1,67 +0,0 @@
-import qbs.File
-import qbs.TextFile
-import "util.js" as Utils
-
-Product {
- type: ["final1", "final2"]
- Group {
- files: ["dummy1.input"]
- fileTags: ["input1"]
- }
- Group {
- files: ["dummy2.input"]
- fileTags: ["input2"]
- }
- Rule {
- inputs: ["input1"]
- Artifact {
- filePath: project.buildDirectory + "/dummy1.final"
- fileTags: ["final1"]
- }
- prepare: {
- var cmds = [];
- for (var i = 0; i < 10; ++i) {
- var cmd = new JavaScriptCommand();
- cmd.silent = true;
- cmd.createFile = i == 9;
- cmd.sourceCode = function() {
- if (createFile) {
- console.info("Creating file");
- var file = new TextFile(output.filePath, TextFile.WriteOnly);
- file.close();
- }
- };
- cmds.push(cmd);
- }
- return cmds;
- }
- }
- Rule {
- inputs: ["input2"]
- Artifact {
- filePath: "dummy.intermediate"
- fileTags: ["intermediate"]
- }
- prepare: {
- var cmd = new JavaScriptCommand();
- cmd.silent = true;
- cmd.sourceCode = function() { };
- return [cmd];
- }
- }
- Rule {
- inputs: ["intermediate"]
- outputFileTags: "final2"
- prepare: {
- do
- Utils.sleep(6000);
- while (!File.exists(project.buildDirectory + "/dummy1.final"));
- var cmd = new JavaScriptCommand();
- cmd.silent = true;
- cmd.sourceCode = function() { };
- return [cmd];
- }
- }
-}
-
-
diff --git a/tests/auto/blackbox/testdata/concurrent-executor/dummy1.input b/tests/auto/blackbox/testdata/concurrent-executor/dummy1.input
deleted file mode 100644
index e69de29bb..000000000
--- a/tests/auto/blackbox/testdata/concurrent-executor/dummy1.input
+++ /dev/null
diff --git a/tests/auto/blackbox/testdata/concurrent-executor/dummy2.input b/tests/auto/blackbox/testdata/concurrent-executor/dummy2.input
deleted file mode 100644
index e69de29bb..000000000
--- a/tests/auto/blackbox/testdata/concurrent-executor/dummy2.input
+++ /dev/null
diff --git a/tests/auto/blackbox/testdata/concurrent-executor/util.js b/tests/auto/blackbox/testdata/concurrent-executor/util.js
deleted file mode 100644
index a37a8cbb1..000000000
--- a/tests/auto/blackbox/testdata/concurrent-executor/util.js
+++ /dev/null
@@ -1,8 +0,0 @@
-function sleep(timeInMs)
-{
- var referenceTime = new Date();
- var time = null;
- do {
- time = new Date();
- } while (time - referenceTime < timeInMs);
-}
diff --git a/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs b/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs
index c40f22736..5c259ae9b 100644
--- a/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs
@@ -4,8 +4,9 @@ BaseApp {
inputNames: ["tool.1", "tool.2"]
inputSearchPaths: "bin"
inputCandidateFilter: {
+ var fi = FileInfo;
return function(f) {
- return FileInfo.fileName(f) == "tool.2";
+ return fi.fileName(f) == "tool.2";
}
}
outputFilePaths: ["bin/tool.2"]
diff --git a/tests/auto/blackbox/testdata/require-deprecated/blubb.js b/tests/auto/blackbox/testdata/require-deprecated/blubb.js
deleted file mode 100644
index 9acc13968..000000000
--- a/tests/auto/blackbox/testdata/require-deprecated/blubb.js
+++ /dev/null
@@ -1,13 +0,0 @@
-var TextFile = loadExtension("qbs.TextFile")
-var zort = loadFile("zort.js")
-
-function createCommands(filePaths) {
- var cmd = new JavaScriptCommand();
- cmd.description = "Write an empty file";
- cmd.filePath = filePaths[0];
- cmd.sourceCode = function() {
- var f = new TextFile(filePath, TextFile.WriteOnly);
- f.close();
- }
- return [cmd, zort.createCommand(filePaths)];
-}
diff --git a/tests/auto/blackbox/testdata/require-deprecated/require.qbs b/tests/auto/blackbox/testdata/require-deprecated/require.qbs
deleted file mode 100644
index 87d8b054b..000000000
--- a/tests/auto/blackbox/testdata/require-deprecated/require.qbs
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'blubb.js' as blubb
-
-Product {
- type: ["text"]
- Rule {
- multiplex: true
- Artifact {
- fileTags: ["text"]
- filePath: "one.txt"
- }
- Artifact {
- fileTags: ["text"]
- filePath: "two.txt"
- }
- prepare: {
- var filePaths = outputs.text.map(function (artifact) {return artifact.filePath; });
- return blubb.createCommands(filePaths);
- }
- }
-}
-
diff --git a/tests/auto/blackbox/testdata/require-deprecated/zort.js b/tests/auto/blackbox/testdata/require-deprecated/zort.js
deleted file mode 100644
index 0dcffb767..000000000
--- a/tests/auto/blackbox/testdata/require-deprecated/zort.js
+++ /dev/null
@@ -1,11 +0,0 @@
-var File = loadExtension("qbs.File")
-
-function createCommand(filePaths) {
- var cmd = new JavaScriptCommand();
- cmd.description = "Create another empty file";
- cmd.filePaths = filePaths;
- cmd.sourceCode = function() {
- File.copy(filePaths[0], filePaths[1]);
- };
- return cmd;
-}
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index d97dd4562..26f29ed0e 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -1782,13 +1782,6 @@ void TestBlackbox::clean()
QVERIFY2(symlinkExists(symLink), qPrintable(symLink));
}
-void TestBlackbox::concurrentExecutor()
-{
- QDir::setCurrent(testDataDir + "/concurrent-executor");
- QCOMPARE(runQbs(QStringList() << "-j" << "2"), 0);
- QVERIFY2(!m_qbsStderr.contains("ASSERT"), m_qbsStderr.constData());
-}
-
void TestBlackbox::conditionalExport()
{
QDir::setCurrent(testDataDir + "/conditional-export");
@@ -2505,8 +2498,8 @@ void TestBlackbox::referenceErrorInExport()
QbsRunParameters params;
params.expectFailure = true;
QVERIFY(runQbs(params) != 0);
- QVERIFY(m_qbsStderr.contains(
- "referenceErrorInExport.qbs:15:12 ReferenceError: Can't find variable: includePaths"));
+ QVERIFY2(m_qbsStderr.contains("referenceErrorInExport.qbs:5:27 'includePaths' is not defined"),
+ m_qbsStderr.constData());
}
void TestBlackbox::removeDuplicateLibraries_data()
@@ -5297,16 +5290,6 @@ void TestBlackbox::require()
QCOMPARE(runQbs(), 0);
}
-void TestBlackbox::requireDeprecated()
-{
- QDir::setCurrent(testDataDir + "/require-deprecated");
- QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStderr.contains("loadExtension() function is deprecated"),
- m_qbsStderr.constData());
- QVERIFY2(m_qbsStderr.contains("loadFile() function is deprecated"),
- m_qbsStderr.constData());
-}
-
void TestBlackbox::rescueTransformerData()
{
QDir::setCurrent(testDataDir + "/rescue-transformer-data");
@@ -7762,6 +7745,11 @@ void TestBlackbox::nodejs()
QSKIP("nodejs.packageManagerFilePath not set and automatic detection failed");
}
+ if (p.value("nodejs.interpreterFilePath").toString().isEmpty()
+ && status != 0 && m_qbsStderr.contains("interpreterPath")) {
+ QSKIP("nodejs.interpreterFilePath not set and automatic detection failed");
+ }
+
if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(status, 0);
diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h
index 429144038..8f9bfc984 100644
--- a/tests/auto/blackbox/tst_blackbox.h
+++ b/tests/auto/blackbox/tst_blackbox.h
@@ -82,7 +82,6 @@ private slots:
void combinedSources();
void commandFile();
void compilerDefinesByLanguage();
- void concurrentExecutor();
void conditionalExport();
void conditionalFileTagger();
void configure();
@@ -285,7 +284,6 @@ private slots:
void reproducibleBuild();
void reproducibleBuild_data();
void require();
- void requireDeprecated();
void rescueTransformerData();
void responseFiles();
void retaggedOutputArtifact();
diff --git a/tests/auto/blackbox/tst_blackboxbase.cpp b/tests/auto/blackbox/tst_blackboxbase.cpp
index ed0b90b55..8e85640db 100644
--- a/tests/auto/blackbox/tst_blackboxbase.cpp
+++ b/tests/auto/blackbox/tst_blackboxbase.cpp
@@ -104,6 +104,8 @@ int TestBlackboxBase::runQbs(const QbsRunParameters &params)
qDebug("%s", qPrintable(process.errorString()));
}
exitCode = -1;
+ } else if (m_qbsStdout.contains("Memory leak:")) {
+ exitCode = 27;
} else {
exitCode = process.exitCode();
}
diff --git a/tests/auto/buildgraph/CMakeLists.txt b/tests/auto/buildgraph/CMakeLists.txt
index 3d9d0348d..d8a9a4bbe 100644
--- a/tests/auto/buildgraph/CMakeLists.txt
+++ b/tests/auto/buildgraph/CMakeLists.txt
@@ -3,5 +3,5 @@ add_qbs_test(buildgraph
tst_buildgraph.cpp
tst_buildgraph.h
DEPENDS
- qbsscriptengine
+ qbsquickjsheaders
)
diff --git a/tests/auto/language/CMakeLists.txt b/tests/auto/language/CMakeLists.txt
index b008f52d7..9c04b6c8d 100644
--- a/tests/auto/language/CMakeLists.txt
+++ b/tests/auto/language/CMakeLists.txt
@@ -2,7 +2,8 @@ add_qbs_test(language
DEFINES
"QBS_VERSION=\"${QBS_VERSION}\""
DEPENDS
- qbsscriptengine
+ qbsquickjsheaders
+ Qt6Core5Compat
SOURCES
tst_language.cpp
tst_language.h
diff --git a/tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs b/tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs
index 965fd36f7..24b246604 100644
--- a/tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs
+++ b/tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs
@@ -1,5 +1,3 @@
-import qbs // FIXME: Don't remove this import because then the test fails!
-
Product {
name: "p"
multiplexByQbsProperties: "architectures"
diff --git a/tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs b/tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs
index e6ed35d42..d6c057a9e 100644
--- a/tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs
+++ b/tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs
@@ -1,5 +1,3 @@
-import qbs // FIXME: Don't remove this import because then the test fails!
-
Product {
name: "p"
multiplexByQbsProperties: ["architectures", "buildVariants", "architectures"]
diff --git a/tests/auto/language/testdata/erroneous/original-in-export-item.qbs b/tests/auto/language/testdata/erroneous/original-in-export-item.qbs
index 06adeb111..c83601cfb 100644
--- a/tests/auto/language/testdata/erroneous/original-in-export-item.qbs
+++ b/tests/auto/language/testdata/erroneous/original-in-export-item.qbs
@@ -1,5 +1,3 @@
-import qbs // FIXME: Don't remove this import because then the test fails!
-
Project {
Product {
name: "a"
diff --git a/tests/auto/language/testdata/erroneous/original-in-export-item2.qbs b/tests/auto/language/testdata/erroneous/original-in-export-item2.qbs
index 68b83118f..1c9f3de4b 100644
--- a/tests/auto/language/testdata/erroneous/original-in-export-item2.qbs
+++ b/tests/auto/language/testdata/erroneous/original-in-export-item2.qbs
@@ -1,5 +1,3 @@
-import qbs // FIXME: Don't remove this import because then the test fails!
-
Project {
Product {
name: "a"
diff --git a/tests/auto/language/testdata/erroneous/original-in-export-item3.qbs b/tests/auto/language/testdata/erroneous/original-in-export-item3.qbs
index 30b30dd0e..d23ad0a08 100644
--- a/tests/auto/language/testdata/erroneous/original-in-export-item3.qbs
+++ b/tests/auto/language/testdata/erroneous/original-in-export-item3.qbs
@@ -1,5 +1,3 @@
-import qbs // FIXME: Don't remove this import because then the test fails!
-
Project {
Product {
name: "a"
diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp
index cdcff99bd..89bda3ab1 100644
--- a/tests/auto/language/tst_language.cpp
+++ b/tests/auto/language/tst_language.cpp
@@ -308,6 +308,9 @@ void TestLanguage::chainedProbes()
const TopLevelProjectConstPtr project = loader->loadProject(parameters);
QVERIFY(!!project);
QCOMPARE(project->products.size(), size_t(1));
+ const QString prop1Val = project->products.front()->moduleProperties
+ ->moduleProperty("m", "prop1").toString();
+ QCOMPARE(prop1Val, QLatin1String("probe1Val"));
const QString prop2Val = project->products.front()->moduleProperties
->moduleProperty("m", "prop2").toString();
QCOMPARE(prop2Val, QLatin1String("probe1Valprobe2Val"));
@@ -805,11 +808,11 @@ void TestLanguage::erroneousFiles_data()
QTest::newRow("importloop1")
<< "Loop detected when importing";
QTest::newRow("nonexistentouter")
- << "Can't find variable: outer";
+ << "'outer' is not defined";
QTest::newRow("invalid_file")
<< "does not exist";
QTest::newRow("invalid-parameter-rhs")
- << "ReferenceError: Can't find variable: access";
+ << "'access' is not defined";
QTest::newRow("invalid-parameter-type")
<< "Value assigned to property 'stringParameter' does not have type 'string'.";
QTest::newRow("invalid_property_type")
@@ -874,8 +877,8 @@ void TestLanguage::erroneousFiles_data()
QTest::newRow("wrongQbsVersionFormat")
<< "The value '.*' of Project.minimumQbsVersion is not a valid version string.";
QTest::newRow("properties-item-with-invalid-condition")
- << "properties-item-with-invalid-condition.qbs:4:19.*TypeError: Result of expression "
- "'cpp.nonexistingproperty'";
+ << "properties-item-with-invalid-condition.qbs:4:19.*"
+ "cannot read property 'contains' of undefined";
QTest::newRow("misused-inherited-property") << "Binding to non-item property";
QTest::newRow("undeclared_property_in_Properties_item") << "Item 'blubb' is not declared";
QTest::newRow("same-module-prefix1") << "The name of module 'prefix1' is equal to the first "
@@ -889,7 +892,7 @@ void TestLanguage::erroneousFiles_data()
QTest::newRow("missing-colon")
<< "Invalid item 'dummy.cxxFlags'. Did you mean to set a module property?";
QTest::newRow("syntax-error-in-probe")
- << "syntax-error-in-probe.qbs:4:20.*ReferenceError";
+ << "syntax-error-in-probe.qbs:4:20.*'fngkgsdjfgklkf' is not defined";
QTest::newRow("wrong-toplevel-item")
<< "wrong-toplevel-item.qbs:1:1.*The top-level item must be of type 'Project' or "
"'Product', but it is of type 'Artifact'.";
@@ -914,10 +917,10 @@ void TestLanguage::erroneousFiles_data()
<< "module-with-invalid-original.qbs:2:24.*The special value 'original' cannot be used "
"on the right-hand side of a property declaration.";
QTest::newRow("original-in-export-item")
- << "original-in-export-item.qbs:7:32.*The special value 'original' cannot be used "
+ << "original-in-export-item.qbs:5:32.*The special value 'original' cannot be used "
"on the right-hand side of a property declaration.";
QTest::newRow("original-in-export-item2")
- << "original-in-export-item2.qbs:6:9.*Item 'x.y' is not declared. Did you forget "
+ << "original-in-export-item2.qbs:4:9.*Item 'x.y' is not declared. Did you forget "
"to add a Depends item";
QTest::newRow("original-in-export-item3")
<< "original-in-export-item3.qbs:6:9.*Item 'x.y' is not declared. Did you forget "
@@ -938,9 +941,9 @@ void TestLanguage::erroneousFiles_data()
<< "dependency-profile-mismatch-2.qbs:15:9 Dependency from product 'main' to "
"product 'dep' not fulfilled. There are no eligible multiplex candidates.";
QTest::newRow("duplicate-multiplex-value")
- << "duplicate-multiplex-value.qbs:3:1.*Duplicate entry 'x86' in qbs.architectures.";
+ << "duplicate-multiplex-value.qbs:1:1.*Duplicate entry 'x86' in qbs.architectures.";
QTest::newRow("duplicate-multiplex-value2")
- << "duplicate-multiplex-value2.qbs:3:1.*Duplicate entry 'architecture' in "
+ << "duplicate-multiplex-value2.qbs:1:1.*Duplicate entry 'architecture' in "
"Product.multiplexByQbsProperties.";
QTest::newRow("invalid-references")
<< "invalid-references.qbs:2:17.*Cannot open '.*nosuchproject.qbs'";
@@ -1603,12 +1606,13 @@ void TestLanguage::itemPrototype()
item->setProperty("z", sourceValueCreator.create("2"));
Evaluator evaluator(m_engine.get());
- QCOMPARE(evaluator.property(proto, "x").toVariant().toInt(), 1);
- QCOMPARE(evaluator.property(proto, "y").toVariant().toInt(), 1);
- QVERIFY(!evaluator.property(proto, "z").isValid());
- QCOMPARE(evaluator.property(item, "x").toVariant().toInt(), 1);
- QCOMPARE(evaluator.property(item, "y").toVariant().toInt(), 2);
- QCOMPARE(evaluator.property(item, "z").toVariant().toInt(), 2);
+ JSContext * const ctx = m_engine->context();
+ QCOMPARE(getJsVariant(ctx, evaluator.property(proto, "x")).toInt(), 1);
+ QCOMPARE(getJsVariant(ctx, evaluator.property(proto, "y")).toInt(), 1);
+ QVERIFY(JS_IsUndefined(evaluator.property(proto, "z")));
+ QCOMPARE(getJsVariant(ctx, evaluator.property(item, "x")).toInt(), 1);
+ QCOMPARE(getJsVariant(ctx, evaluator.property(item, "y")).toInt(), 2);
+ QCOMPARE(getJsVariant(ctx, evaluator.property(item, "z")).toInt(), 2);
}
void TestLanguage::itemScope()
@@ -1627,10 +1631,11 @@ void TestLanguage::itemScope()
item->setProperty("z", sourceValueCreator.create("x + y"));
Evaluator evaluator(m_engine.get());
- QCOMPARE(evaluator.property(scope1, "x").toVariant().toInt(), 1);
- QCOMPARE(evaluator.property(scope2, "y").toVariant().toInt(), 2);
- QVERIFY(!evaluator.property(scope2, "x").isValid());
- QCOMPARE(evaluator.property(item, "z").toVariant().toInt(), 3);
+ JSContext * const ctx = m_engine->context();
+ QCOMPARE(getJsVariant(ctx, evaluator.property(scope1, "x")).toInt(), 1);
+ QCOMPARE(getJsVariant(ctx, evaluator.property(scope2, "y")).toInt(), 2);
+ QVERIFY(JS_IsUndefined(evaluator.property(scope2, "x")));
+ QCOMPARE(getJsVariant(ctx, evaluator.property(item, "z")).toInt(), 3);
}
void TestLanguage::jsExtensions()
@@ -1640,10 +1645,10 @@ void TestLanguage::jsExtensions()
QTextStream ts(&file);
QString code = ts.readAll();
QVERIFY(!code.isEmpty());
- QScriptValue evaluated = m_engine->evaluate(code, file.fileName(), 1);
- if (m_engine->hasErrorOrException(evaluated)) {
- qDebug() << m_engine->uncaughtExceptionBacktrace();
- QFAIL(qPrintable(m_engine->lastErrorString(evaluated)));
+ m_engine->evaluate(JsValueOwner::Caller, code, file.fileName(), 1);
+ if (JsException ex = m_engine->checkAndClearException({})) {
+ qDebug() << ex.stackTrace();
+ QFAIL(qPrintable(ex.message()));
}
}
diff --git a/tests/auto/pkgconfig/CMakeLists.txt b/tests/auto/pkgconfig/CMakeLists.txt
index 4d60491ba..74a13a8ab 100644
--- a/tests/auto/pkgconfig/CMakeLists.txt
+++ b/tests/auto/pkgconfig/CMakeLists.txt
@@ -4,5 +4,5 @@ add_qbs_test(pkgconfig
tst_pkgconfig.h
DEPENDS
qbspkgconfig
- qbsscriptengine
+ qbsquickjsheaders
)