From 087c22e17721f37490dd2048a567b6a58065d939 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 21 Feb 2022 15:02:31 +0100 Subject: Switch JavaScript back-end Newer clang versions seem to expose serious bugs in QtScript, whose complexity makes it difficult to track them down. We therefore switch to the more light-weight QuickJS, which offers all the features we need (most notably property access interception), as well as good performance. To save some porting effort, we removed the long-deprecated loadFile() and loadExtension() functions. During the porting procedure, we noticed and fixed thread safety issues in artifact access from JS commands. We consider this change important enough to bump the major version, so the next release will be 2.0. Detailed benchmarking data is below. In summary, we see a modest speed- up at the cost of a similarly modest increase in memory consumption (with the exception of project resolving on macOS, which has become a bit slower). Importantly, the increase does not rise with project size, as the comparison of qbs vs Qt Creator shows. Output of qbs_benchmarker on Linux with qbs as test project: ========== Performance data for Resolving ========== Old instruction count: 12870602895 New instruction count: 11923459780 Relative change: -8 % Old peak memory usage: 61775848 Bytes New peak memory usage: 67583424 Bytes Relative change: +9 % ========== Performance data for Rule Execution ========== Old instruction count: 4074062223 New instruction count: 3887473574 Relative change: -5 % Old peak memory usage: 35123704 Bytes New peak memory usage: 38398392 Bytes Relative change: +9 % ========== Performance data for Null Build ========== Old instruction count: 1104417596 New instruction count: 1011033948 Relative change: -9 % Old peak memory usage: 24461824 Bytes New peak memory usage: 25325920 Bytes Relative change: +3 % Output of qbs_benchmarker on Linux with Qt Creator as test project: ========== Performance data for Resolving ========== Old instruction count: 67166450352 New instruction count: 60772791018 Relative change: -10 % Old peak memory usage: 327011616 Bytes New peak memory usage: 343724176 Bytes Relative change: +5 % ========== Performance data for Rule Execution ========== Old instruction count: 71684351183 New instruction count: 67051936965 Relative change: -7 % Old peak memory usage: 374913688 Bytes New peak memory usage: 387790992 Bytes Relative change: +3 % ========== Performance data for Null Build ========== Old instruction count: 8383156078 New instruction count: 7930705668 Relative change: -6 % Old peak memory usage: 180468360 Bytes New peak memory usage: 182490384 Bytes Relative change: +1 % Real-world data building Qt Creator (using qbs --log-time, several runs, removing outliers): macOS: Resolving: 43s -> 47s Rule execution: 17s -> 14s Windows: Resolving: 18s -> 16s Rule execution: 22s -> 17s Fixes: QBS-913 Fixes: QBS-1103 Fixes: QBS-1126 Fixes: QBS-1227 Fixes: QBS-1684 Change-Id: Ie5088155026e85bbd1e303f1c67addb15810a3cb Reviewed-by: Ivan Komissarov Reviewed-by: Orgad Shaneh --- .clang-tidy | 2 +- .github/actions/download-qt/action.yml | 2 +- .github/workflows/release.yml | 2 +- .gitmodules | 3 - CMakeLists.txt | 4 - VERSION | 2 +- cmake/QbsBuildConfig.cmake | 4 +- doc/codeattributions.qdoc | 49 +- doc/qbs.qdoc | 6 - docker/focal/Dockerfile | 2 +- docker/leap/Dockerfile | 2 +- docker/windowsservercore/Dockerfile | 4 +- qbs-resources/imports/QbsUnittest.qbs | 13 +- .../modules/qbsbuildconfig/qbsbuildconfig.qbs | 4 +- scripts/address-sanitizer-suppressions.txt | 2 - scripts/install-qt.sh | 2 +- share/qbs/modules/cpp/msvc.js | 4 +- share/qbs/modules/cpp/sdcc.js | 3 +- share/qbs/modules/cpp/windows-clang-cl.qbs | 3 +- share/qbs/modules/dmg/dmg.js | 6 +- share/qbs/modules/qbs/common.qbs | 5 +- src/lib/CMakeLists.txt | 4 - src/lib/corelib/CMakeLists.txt | 7 +- src/lib/corelib/api/internaljobs.cpp | 3 + src/lib/corelib/api/projectfileupdater.cpp | 2 +- src/lib/corelib/buildgraph/artifact.cpp | 2 + src/lib/corelib/buildgraph/artifact.h | 5 + .../corelib/buildgraph/artifactsscriptvalue.cpp | 257 +- src/lib/corelib/buildgraph/artifactsscriptvalue.h | 13 +- src/lib/corelib/buildgraph/buildgraph.cpp | 503 +- src/lib/corelib/buildgraph/buildgraph.h | 13 +- .../buildgraph/dependencyparametersscriptvalue.cpp | 26 +- .../buildgraph/dependencyparametersscriptvalue.h | 7 +- src/lib/corelib/buildgraph/depscanner.cpp | 53 +- src/lib/corelib/buildgraph/depscanner.h | 7 +- .../corelib/buildgraph/environmentscriptrunner.cpp | 44 +- src/lib/corelib/buildgraph/jscommandexecutor.cpp | 64 +- .../corelib/buildgraph/processcommandexecutor.cpp | 47 +- src/lib/corelib/buildgraph/productbuilddata.cpp | 2 - src/lib/corelib/buildgraph/productbuilddata.h | 3 + src/lib/corelib/buildgraph/projectbuilddata.cpp | 2 +- src/lib/corelib/buildgraph/projectbuilddata.h | 6 +- src/lib/corelib/buildgraph/qtmocscanner.cpp | 57 +- src/lib/corelib/buildgraph/qtmocscanner.h | 14 +- src/lib/corelib/buildgraph/rulecommands.cpp | 318 +- src/lib/corelib/buildgraph/rulecommands.h | 19 +- src/lib/corelib/buildgraph/rulesapplicator.cpp | 228 +- src/lib/corelib/buildgraph/rulesapplicator.h | 12 +- .../corelib/buildgraph/rulesevaluationcontext.cpp | 27 +- .../corelib/buildgraph/rulesevaluationcontext.h | 13 +- .../buildgraph/scriptclasspropertyiterator.h | 110 - src/lib/corelib/buildgraph/transformer.cpp | 233 +- src/lib/corelib/buildgraph/transformer.h | 26 +- src/lib/corelib/corelib.qbs | 22 +- src/lib/corelib/jsextensions/binaryfile.cpp | 228 +- src/lib/corelib/jsextensions/domxml.cpp | 740 +- .../corelib/jsextensions/environmentextension.cpp | 130 +- src/lib/corelib/jsextensions/file.cpp | 337 +- src/lib/corelib/jsextensions/fileinfoextension.cpp | 410 +- src/lib/corelib/jsextensions/host.cpp | 138 +- src/lib/corelib/jsextensions/jsextension.h | 337 + src/lib/corelib/jsextensions/jsextensions.cpp | 21 +- src/lib/corelib/jsextensions/jsextensions.h | 14 +- src/lib/corelib/jsextensions/moduleproperties.cpp | 393 +- src/lib/corelib/jsextensions/moduleproperties.h | 25 +- src/lib/corelib/jsextensions/pkgconfigjs.cpp | 64 +- src/lib/corelib/jsextensions/pkgconfigjs.h | 27 +- src/lib/corelib/jsextensions/process.cpp | 306 +- src/lib/corelib/jsextensions/propertylist.cpp | 10 +- src/lib/corelib/jsextensions/propertylist_darwin.h | 91 - .../corelib/jsextensions/propertylist_darwin.mm | 285 +- src/lib/corelib/jsextensions/temporarydir.cpp | 85 +- src/lib/corelib/jsextensions/textfile.cpp | 201 +- .../corelib/jsextensions/utilitiesextension.cpp | 724 +- src/lib/corelib/language/evaluationdata.h | 66 - src/lib/corelib/language/evaluator.cpp | 818 +- src/lib/corelib/language/evaluator.h | 52 +- src/lib/corelib/language/evaluatorscriptclass.cpp | 776 - src/lib/corelib/language/evaluatorscriptclass.h | 133 - src/lib/corelib/language/language.cpp | 2 - src/lib/corelib/language/language.h | 4 +- src/lib/corelib/language/loader.cpp | 13 +- src/lib/corelib/language/moduleloader.cpp | 50 +- src/lib/corelib/language/moduleproviderloader.cpp | 12 +- src/lib/corelib/language/preparescriptobserver.cpp | 22 +- src/lib/corelib/language/preparescriptobserver.h | 11 +- src/lib/corelib/language/probesresolver.cpp | 60 +- src/lib/corelib/language/projectresolver.cpp | 41 +- src/lib/corelib/language/propertydeclaration.cpp | 1 - src/lib/corelib/language/scriptengine.cpp | 813 +- src/lib/corelib/language/scriptengine.h | 249 +- src/lib/corelib/language/scriptimporter.cpp | 24 +- src/lib/corelib/language/scriptimporter.h | 9 +- src/lib/corelib/language/scriptpropertyobserver.h | 7 +- src/lib/corelib/tools/codelocation.cpp | 3 +- src/lib/corelib/tools/codelocation.h | 2 + src/lib/corelib/tools/error.cpp | 19 +- src/lib/corelib/tools/progressobserver.h | 9 + src/lib/corelib/tools/scripttools.cpp | 243 +- src/lib/corelib/tools/scripttools.h | 160 +- src/lib/corelib/tools/settingsrepresentation.cpp | 22 +- src/lib/corelib/tools/stringconstants.h | 1 + src/lib/libs.qbs | 1 - src/lib/scriptengine/CMakeLists.txt | 367 - src/lib/scriptengine/scriptengine.qbs | 464 - src/packages/archive/archive.qbs | 1 - src/shared/CMakeLists.txt | 1 + src/shared/bundledqt/bundledqt.qbs | 1 - src/shared/qtscript | 1 - src/shared/quickjs/.clang-tidy | 13 + src/shared/quickjs/CMakeLists.txt | 33 + src/shared/quickjs/LICENSE | 22 + src/shared/quickjs/cutils.c | 630 + src/shared/quickjs/cutils.h | 309 + src/shared/quickjs/libregexp-opcode.h | 58 + src/shared/quickjs/libregexp.c | 2610 + src/shared/quickjs/libregexp.h | 92 + src/shared/quickjs/libunicode-table.h | 4449 ++ src/shared/quickjs/libunicode.c | 1558 + src/shared/quickjs/libunicode.h | 124 + src/shared/quickjs/list.h | 100 + src/shared/quickjs/quickjs-atom.h | 273 + src/shared/quickjs/quickjs-opcode.h | 365 + src/shared/quickjs/quickjs.c | 54315 +++++++++++++++++++ src/shared/quickjs/quickjs.diff | 4061 ++ src/shared/quickjs/quickjs.h | 1020 + src/shared/quickjs/quickjs.qbs | 33 + src/src.qbs | 3 +- tests/auto/api/tst_api.cpp | 4 +- .../concurrent-executor/concurrent-executor.qbs | 67 - .../testdata/concurrent-executor/dummy1.input | 0 .../testdata/concurrent-executor/dummy2.input | 0 .../blackbox/testdata/concurrent-executor/util.js | 8 - .../testdata/path-probe/candidate-filter.qbs | 3 +- .../blackbox/testdata/require-deprecated/blubb.js | 13 - .../testdata/require-deprecated/require.qbs | 21 - .../blackbox/testdata/require-deprecated/zort.js | 11 - tests/auto/blackbox/tst_blackbox.cpp | 26 +- tests/auto/blackbox/tst_blackbox.h | 2 - tests/auto/blackbox/tst_blackboxbase.cpp | 2 + tests/auto/buildgraph/CMakeLists.txt | 2 +- tests/auto/language/CMakeLists.txt | 3 +- .../erroneous/duplicate-multiplex-value.qbs | 2 - .../erroneous/duplicate-multiplex-value2.qbs | 2 - .../testdata/erroneous/original-in-export-item.qbs | 2 - .../erroneous/original-in-export-item2.qbs | 2 - .../erroneous/original-in-export-item3.qbs | 2 - tests/auto/language/tst_language.cpp | 51 +- tests/auto/pkgconfig/CMakeLists.txt | 2 +- 149 files changed, 75512 insertions(+), 6078 deletions(-) delete mode 100644 .gitmodules delete mode 100644 src/lib/corelib/buildgraph/scriptclasspropertyiterator.h create mode 100644 src/lib/corelib/jsextensions/jsextension.h delete mode 100644 src/lib/corelib/jsextensions/propertylist_darwin.h delete mode 100644 src/lib/corelib/language/evaluationdata.h delete mode 100644 src/lib/corelib/language/evaluatorscriptclass.cpp delete mode 100755 src/lib/corelib/language/evaluatorscriptclass.h delete mode 100644 src/lib/scriptengine/CMakeLists.txt delete mode 100644 src/lib/scriptengine/scriptengine.qbs delete mode 160000 src/shared/qtscript create mode 100644 src/shared/quickjs/.clang-tidy create mode 100644 src/shared/quickjs/CMakeLists.txt create mode 100644 src/shared/quickjs/LICENSE create mode 100644 src/shared/quickjs/cutils.c create mode 100644 src/shared/quickjs/cutils.h create mode 100644 src/shared/quickjs/libregexp-opcode.h create mode 100644 src/shared/quickjs/libregexp.c create mode 100644 src/shared/quickjs/libregexp.h create mode 100644 src/shared/quickjs/libunicode-table.h create mode 100644 src/shared/quickjs/libunicode.c create mode 100644 src/shared/quickjs/libunicode.h create mode 100644 src/shared/quickjs/list.h create mode 100644 src/shared/quickjs/quickjs-atom.h create mode 100644 src/shared/quickjs/quickjs-opcode.h create mode 100644 src/shared/quickjs/quickjs.c create mode 100644 src/shared/quickjs/quickjs.diff create mode 100644 src/shared/quickjs/quickjs.h create mode 100644 src/shared/quickjs/quickjs.qbs delete mode 100644 tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs delete mode 100644 tests/auto/blackbox/testdata/concurrent-executor/dummy1.input delete mode 100644 tests/auto/blackbox/testdata/concurrent-executor/dummy2.input delete mode 100644 tests/auto/blackbox/testdata/concurrent-executor/util.js delete mode 100644 tests/auto/blackbox/testdata/require-deprecated/blubb.js delete mode 100644 tests/auto/blackbox/testdata/require-deprecated/require.qbs delete mode 100644 tests/auto/blackbox/testdata/require-deprecated/zort.js 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 @@ -642,11 +641,6 @@ \li \c false \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" 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 #include #include +#include #include #include #include @@ -76,6 +77,8 @@ public: { std::lock_guard 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 +#include #include #include @@ -123,8 +124,12 @@ public: void load(PersistentPool &pool) override; void store(PersistentPool &pool) override; + using Deregister = std::function; + void setDeregister(const Deregister &deregister) { m_deregister = deregister; } + private: FileTags m_fileTags; + Deregister m_deregister; }; template<> inline QString Set::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 #include - -#include -#include +#include namespace qbs { namespace Internal { -enum BuildGraphScriptValueCommonPropertyKeys : quint32 { - CachedValueKey, - FileTagKey, - ProductPtrKey, -}; - -class ArtifactsScriptClass : public QScriptClass +template +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) { + 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( - object.data().property(ProductPtrKey).toVariant().value()); - } - } - - ScriptEngine *qbsEngine() const { return static_cast(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 +static ArtifactSetByFileTag artifactsMap(const ProductOrModule *productOrModule) { - return product->buildData->artifactsByFileTag(); + if constexpr (std::is_same_v) + return productOrModule->buildData->artifactsByFileTag(); + else + return artifactsMap(productOrModule->product); } -static ArtifactSetByFileTag artifactsMap(const ResolvedModule *module) +template static int scriptClassIndex() { - return artifactsMap(module->product); + if constexpr (std::is_same_v) + return 0; + return 1; } -static QScriptValue createArtifactsObject(const ResolvedProduct *product, ScriptEngine *engine) +template +std::unique_lock 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(reinterpret_cast(product)); - data.setProperty(ProductPtrKey, engine->newVariant(v)); - artifactsObj.setData(data); - return artifactsObj; + if constexpr (std::is_same_v) + return productOrModule->buildData->getArtifactsMapLock(); + else + return getArtifactsMapLock(productOrModule->product); } -static QScriptValue createArtifactsObject(const ResolvedModule *, ScriptEngine *engine) +template +static bool checkAndSetArtifactsMapUpToDateFlag(const ProductOrModule *productOrModule) { - return engine->newObject(); + if constexpr (std::is_same_v) + return productOrModule->buildData->checkAndSetJsArtifactsMapUpToDateFlag(); + else + return checkAndSetArtifactsMapUpToDateFlag(productOrModule->product); } -static bool checkAndSetArtifactsMapUpToDateFlag(const ResolvedProduct *p) +// Must be called with artifacts map lock held! +template +void registerArtifactsMapAccess(ScriptEngine *engine, ProductOrModule *productOrModule) { - return p->buildData->checkAndSetJsArtifactsMapUpToDateFlag(); + if constexpr (std::is_same_v) { + 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 +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( + obj, engine->artifactsScriptClass(scriptClassIndex())); + const std::unique_lock lock = getArtifactsMapLock(productOrModule); + registerArtifactsMapAccess(engine, productOrModule); + if constexpr (std::is_same_v) + 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(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 static QScriptValue js_artifactsForFileTag( - QScriptContext *ctx, ScriptEngine *engine, const ProductOrModule *productOrModule) +template +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( + obj, engine->artifactsScriptClass(scriptClassIndex())); + 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) + engine->setNonExistingArtifactSetRequested(productOrModule, tagString); + return 1; + } + if constexpr (std::is_same_v) + 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 static QScriptValue js_artifacts( - QScriptContext *ctx, ScriptEngine *engine, const ProductOrModule *productOrModule) +template 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(jsProductOrModule, + engine->dataWithPtrClass()); + JSValue artifactsObj = engine->artifactsMapScriptValue(productOrModule); + if (!JS_IsUndefined(artifactsObj)) return artifactsObj; + const int classIndex = scriptClassIndex(); + 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, + &getArtifactsProperty); + 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); - 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(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(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 -#include - -QT_BEGIN_NAMESPACE -class QScriptContext; -QT_END_NAMESPACE +#include 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 @@ -70,7 +69,6 @@ #include #include -#include #include #include @@ -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(reinterpret_cast(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(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( + 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 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( - object.data().property(ProductPtrKey).toVariant().value()); - } + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const auto product = attachedPointer( + 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(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 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(engine), - dependency.get())); + setupBaseProductScriptValue(engine, dependency.get()); + JSValue obj = JS_NewObjectClass(engine->context(), + engine->productPropertyScriptClass()); + attachPointerTo(obj, dependency.get()); const QVariantMap ¶ms = (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 ¶ms = 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(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( + 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 &properties, ScriptEngine *engine) { - QScriptValue propertiesScriptValue = engine->newArray(static_cast(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(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(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(&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 &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 #include -#include +#include -#include +#include +#include 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 ¶metersMap, ScriptEngine *engine) +JSValue dependencyParametersValue(const QString &productName, const QString &dependencyName, + const QVariantMap ¶metersMap, 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 + #include -#include namespace qbs { namespace Internal { class ScriptEngine; -QScriptValue dependencyParametersValue(const QString &productName, const QString &dependencyName, - const QVariantMap ¶metersMap, ScriptEngine *engine); +JSValue dependencyParametersValue(const QString &productName, const QString &dependencyName, + const QVariantMap ¶metersMap, 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 -#include +#include 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 #include #include +#include #include -#include - 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 #include +#include + #include #include @@ -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 #include +#include + #include +#include #include #include #include @@ -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 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{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 #include +#include + #include #include #include -#include - 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 l(m_artifactsMapMutex); return m_artifactsByFileTag; } @@ -124,7 +123,6 @@ void ProductBuildData::addRescuableArtifactData(const QString &filePath, bool ProductBuildData::checkAndSetJsArtifactsMapUpToDateFlag() { - std::lock_guard 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 getArtifactsMapLock() { + return std::unique_lock(m_artifactsMapMutex); + } template 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 #include +#include + #include #include -#include - #include 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 -#include -#include - 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(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(input)); + if (argc < 1) + return throwError(ctx, Tr::tr("QtMocScanner.apply() requires an argument.")); + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const auto scanner = attachedPointer(this_val, engine->dataWithPtrClass()); + return scanner->apply(engine, attachedPointer(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(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 +#include #include #include -#include - 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 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 #include #include #include @@ -48,9 +50,6 @@ #include -#include -#include - 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(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(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 #include +#include + #include #include #include -#include - 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 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 - /**************************************************************************** ** ** 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 #include #include -#include #include #include #include @@ -69,7 +65,6 @@ #include #include -#include #include #include @@ -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 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 RulesApplicator::runOutputArtifactsScript(const ArtifactSet &inputArtifacts, - const QScriptValueList &args) + const JSValueList &args) { QList 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 m_propertyValues; static Set 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 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 #include +#include #include +#include #include #include #include -#include #include @@ -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 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 #include #include @@ -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 #include +#include + #include #include -#include -#include - 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 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 - -#include -#include -#include -#include -#include - -#include - -namespace qbs { -namespace Internal { - -class ScriptClassPropertyIterator : public QScriptClassPropertyIterator -{ -public: - ScriptClassPropertyIterator(const QScriptValue &object, const QVariantMap &properties, - std::vector 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 m_it; - const std::vector 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 #include #include @@ -54,6 +55,7 @@ #include #include +#include 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(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(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>; 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 &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(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(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(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(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 #include +#include + #include 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 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 #include #include @@ -46,84 +48,99 @@ #include #include -#include -#include - -#include -#include -#include namespace qbs { namespace Internal { -class BinaryFile : public QObject, public QScriptable, public ResourceAcquiringScriptObject +class BinaryFile : public JsExtension { - Q_OBJECT + friend class JsExtension; 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 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(context->argument(1).toInt32())); - break; - default: - return context->throwError(Tr::tr("BinaryFile constructor takes at most two parameters.")); - } - - const auto se = static_cast(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(ctx, "BinaryFile constructor", argc, argv); + OpenMode mode = ReadOnly; + if (argc > 1) { + mode = static_cast + (fromArg(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(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(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(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(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 #include -#include #include -#include -#include -#include - #include namespace qbs { namespace Internal { -class XmlDomDocument; - -class XmlDomNode: public QObject, public QScriptable +template class XmlDomNode : public JsExtension> { - 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(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::template getArgument + (ctx, "DomNode.attribute", argc, argv); + QString defaultValue; + if (argc > 1) + defaultValue = JsExtension::template fromArg + (ctx, "DomNode.attribute", 2, argv[1]); + return makeJsString(ctx, JsExtension::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>::createObjectDirect( + ctx, JsExtension::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::template getArgument + (ctx, "DomNode.firstChild", argc, argv); + return JsExtension>::createObjectDirect + (ctx, JsExtension::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::template getArgument + (ctx, "DomNode.lastChild", argc, argv); + return JsExtension>::createObjectDirect + (ctx, JsExtension::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::template getArgument + (ctx, "DomNode.previousSibling", argc, argv); + return JsExtension>::createObjectDirect(ctx, JsExtension::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::template getArgument + (ctx, "Xml.DomElement.nextSibling", argc, argv); + return JsExtension>::createObjectDirect + (ctx, JsExtension::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 *parentNode() const + { + return new XmlDomNode(m_value.parentNode()); + } + XmlDomNode *firstChild(const QString &tagName) + { + if (tagName.isEmpty()) + return new XmlDomNode(m_value.firstChild()); + return new XmlDomNode(m_value.firstChildElement(tagName)); + } + XmlDomNode *lastChild(const QString &tagName) const + { + if (tagName.isEmpty()) + return new XmlDomNode(m_value.lastChild()); + return new XmlDomNode(m_value.lastChildElement(tagName)); + } + XmlDomNode *previousSibling(const QString &tagName) const + { + if (tagName.isEmpty()) + return new XmlDomNode(m_value.previousSibling()); + return new XmlDomNode(m_value.previousSiblingElement(tagName)); + } + XmlDomNode *nextSibling(const QString &tagName) const + { + if (tagName.isEmpty()) + return new XmlDomNode(m_value.nextSibling()); + return new XmlDomNode(m_value.nextSiblingElement(tagName)); + } + void appendChild(const XmlDomNode *newChild) + { + m_value.appendChild(newChild->value()); + } + void insertBefore(const XmlDomNode *newChild, const XmlDomNode *refChild) + { + m_value.insertBefore(newChild->value(), refChild->value()); + } + void insertAfter(const XmlDomNode *newChild, const XmlDomNode *refChild) + { + m_value.insertAfter(newChild->value(), refChild->value()); + } + void replaceChild(const XmlDomNode *newChild, const XmlDomNode *oldChild) + { + m_value.replaceChild(newChild->value(), oldChild->value()); + } + void removeChild(const XmlDomNode *oldChild) + { + m_value.removeChild(oldChild->value()); + } -bool XmlDomNode::isCDATASection() const -{ - return m_domNode.isCDATASection(); -} + // Implemented for QDomDocument only. + XmlDomNode *documentElement(); + XmlDomNode *createElement(const QString &tagName); + XmlDomNode *createCDATASection(const QString &value); + XmlDomNode *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::name() { return "DomNode"; } +template<> const char *XmlDomNode::name() { return "DomDocument"; } -QString XmlDomNode::tagName() const +template<> JSValue XmlDomNode::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::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(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 *XmlDomNode::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(m_value.documentElement()); } -QString XmlDomNode::data() const +template<> XmlDomNode *XmlDomNode::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(m_value.createElement(tagName)); } -void XmlDomNode::setData(const QString &v) const +template<> XmlDomNode *XmlDomNode::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(m_value.createCDATASection(value)); } -void XmlDomNode::clear() +template<> XmlDomNode *XmlDomNode::createTextNode(const QString &value) { - m_domNode.clear(); + return new XmlDomNode(m_value.createTextNode(value)); } -bool XmlDomNode::hasAttributes() const +template<> bool XmlDomNode::setContent(const QString &content) { - return m_domNode.hasAttributes(); + return m_value.setContent(content); } -bool XmlDomNode::hasChildNodes() const +template<> QString XmlDomNode::toString(int indent) { - return m_domNode.hasChildNodes(); + return m_value.toString(indent); } -QScriptValue XmlDomNode::parentNode() const +template<> JSValue XmlDomNode::jsDocumentElement(JSContext *ctx, JSValue this_val, + int, JSValue *) { - return engine()->newQObject(new XmlDomNode(m_domNode.parentNode()), QScriptEngine::ScriptOwnership); + return XmlDomNode::createObjectDirect( + ctx, fromJsObject(ctx, this_val)->documentElement()); } -QScriptValue XmlDomNode::firstChild(const QString &tagName) +template<> JSValue XmlDomNode::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(ctx, "DomDocument.createElement", argc, argv); + const JSValue obj = XmlDomNode::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::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(ctx, "DomDocument.createCDATASection", argc, argv); + return XmlDomNode::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::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(ctx, "DomDocument.createTextNode", argc, argv); + return XmlDomNode::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::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(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::jsToString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - auto newNode = qobject_cast(newChild.toQObject()); - if (!newNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; + try { + qint32 indent = 1; + if (argc > 0) + indent = getArgument(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::save(const QString &filePath, int indent) { - auto newNode = qobject_cast(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(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::jsSave(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - auto newNode = qobject_cast(newChild.toQObject()); - if (!newNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; - } - - auto refNode = qobject_cast(refChild.toQObject()); - if (!refNode) { - context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object")); - return {}; + try { + const auto filePath = getArgument(ctx, "DomDocument.save", argc, argv); + qint32 indent = 1; + if (argc > 1) + indent = fromArg(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::load(const QString &filePath) { - auto newNode = qobject_cast(newChild.toQObject()); - if (!newNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; - } - - auto oldNode = qobject_cast(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::jsLoad(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - auto oldNode = qobject_cast(oldChild.toQObject()); - if (!oldNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; + try { + const auto filePath = getArgument(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::XmlDomNode(JSContext *, const QString &name) : m_value(name) {} + +template +void XmlDomNode::setupMethods(JSContext *ctx, JSValue obj) +{ + JsExtension::setupMethod(ctx, obj, "appendChild", &jsAppendChild, 1); + JsExtension::setupMethod(ctx, obj, "attribute", &jsAttribute, 2); + JsExtension::setupMethod(ctx, obj, "clear", &jsClear, 0); + JsExtension::setupMethod(ctx, obj, "data", &jsData, 0); + JsExtension::setupMethod(ctx, obj, "firstChild", &jsFirstChild, 1); + JsExtension::setupMethod(ctx, obj, "hasAttribute", &jsHasAttribute, 1); + JsExtension::setupMethod(ctx, obj, "hasAttributes", &jsHasAttributes, 0); + JsExtension::setupMethod(ctx, obj, "hasChildNodes", &jsHasChildNodes, 0); + JsExtension::setupMethod(ctx, obj, "insertAfter", &jsInsertAfter, 2); + JsExtension::setupMethod(ctx, obj, "insertBefore", &jsInsertBefore, 2); + JsExtension::setupMethod(ctx, obj, "isCDATASection", &jsIsCDATASection, 0); + JsExtension::setupMethod(ctx, obj, "isElement", &jsIsElement, 0); + JsExtension::setupMethod(ctx, obj, "isText", &jsIsText, 0); + JsExtension::setupMethod(ctx, obj, "lastChild", &jsLastChild, 1); + JsExtension::setupMethod(ctx, obj, "nextSibling", &jsNextSibling, 1); + JsExtension::setupMethod(ctx, obj, "parentNode", &jsParentNode, 0); + JsExtension::setupMethod(ctx, obj, "previousSibling", &jsPreviousSibling, 1); + JsExtension::setupMethod(ctx, obj, "removeChild", &jsRemoveChild, 1); + JsExtension::setupMethod(ctx, obj, "replaceChild", &jsReplaceChild, 2); + JsExtension::setupMethod(ctx, obj, "setAttribute", &jsSetAttribute, 2); + JsExtension::setupMethod(ctx, obj, "setData", &jsSetData, 1); + JsExtension::setupMethod(ctx, obj, "setTagName", &jsSetTagName, 1); + JsExtension::setupMethod(ctx, obj, "tagName", &jsTagName, 0); + JsExtension::setupMethod(ctx, obj, "text", &jsText, 0); + if constexpr (std::is_same_v) { + JsExtension::setupMethod(ctx, obj, "documentElement", &jsDocumentElement, 0); + JsExtension::setupMethod(ctx, obj, "createElement", &jsCreateElement, 1); + JsExtension::setupMethod(ctx, obj, "createCDATASection", + &jsCreateCDATASection, 1); + JsExtension::setupMethod(ctx, obj, "createTextNode", &jsCreateTextNode, 1); + JsExtension::setupMethod(ctx, obj, "setContent", &jsSetContent, 1); + JsExtension::setupMethod(ctx, obj, "toString", &jsToString, 1); + JsExtension::setupMethod(ctx, obj, "save", &jsSave, 2); + JsExtension::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::registerClass(engine, contextObject); + XmlDomNode::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 #include #include @@ -44,93 +46,89 @@ #include -#include -#include - namespace qbs { namespace Internal { -class EnvironmentExtension : public QObject, QScriptable +class EnvironmentExtension : public JsExtension { - 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(v.value()); - 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(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(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(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(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(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 #include #include @@ -44,15 +46,11 @@ #include #include -#include -#include - namespace qbs { namespace Internal { -class File : public QObject, QScriptable +class File : public JsExtension { - 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(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(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(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(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(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(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(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(ctx, "Environment.directoryEntries", + argc, argv); + const QString path = std::get<0>(args); + const QDir dir(path); + const auto filters = static_cast(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(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(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(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(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(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(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(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(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(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 #include #include @@ -45,9 +47,6 @@ #include #include -#include -#include - #include namespace qbs { @@ -63,300 +62,253 @@ static QString uniqueSeparators(QString path) return path; } -class FileInfoExtension : public QObject, QScriptable +class FileInfoExtension : public JsExtension { - 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(ctx, "FileInfo.path", argc, argv); + HostOsInfo::HostOs hostOs = HostOsInfo::hostOs(); + if (argc > 1) { + const auto osList = fromArg(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(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(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(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(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(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(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(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(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(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(ctx, "FileInfo.isAbsolutePath", argc, argv); + HostOsInfo::HostOs hostOs = HostOsInfo::hostOs(); + if (argc > 1) { + const auto osList = fromArg(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(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(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(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(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(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 #include #include @@ -44,27 +46,28 @@ #include #include -#include -#include - namespace qbs { namespace Internal { -class Host : public QObject, QScriptable +class Host : public JsExtension { - 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 +#include + +#include +#include +#include + +namespace qbs::Internal { + +template struct PackHelper {}; +template struct FromArgHelper; +template struct FunctionTrait; + +#define DECLARE_ENUM(ctx, obj, enumVal) \ + JS_SetPropertyStr((ctx), (obj), #enumVal, JS_NewInt32((ctx), (enumVal))) + +template 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 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(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 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 static void setupMethod(JSContext *ctx, JSValue obj, + const QString &name, Func func, int argc) + { + setupMethod(ctx, obj, name.toUtf8().constData(), func, argc); + } + + template static T fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) + { + return FromArgHelper::fromArg(ctx, funcName, pos, v); + } + + template + static void assignToTuple(Tuple &tuple, std::index_sequence, JSContext *ctx, const char *funcName, JSValueConst *argv) { + Q_UNUSED(ctx) + Q_UNUSED(funcName) + Q_UNUSED(argv) + ((std::get(tuple) = fromArg>(ctx, funcName, I + 1, argv[I])), ...); + } + + template static std::tuple 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>...> values; + assignToTuple(values, std::make_index_sequence::value>(), + ctx, funcName, argv); + return values; + } + template static std::tuple getArgumentsHelper(PackHelper, JSContext *ctx, + const char *funcName, int argc, JSValueConst *argv) + { + return getArguments(ctx, funcName, argc, argv); + } + + template static T getArgument(JSContext *ctx, const char *funcName, + int argc, JSValueConst *argv) + { + return std::get<0>(getArguments(ctx, funcName, argc, argv)); + } + + template + static void jsForwardHelperVoid(Derived *obj, Func func, const Tuple &tuple, std::index_sequence) { + (obj->*func)(std::get(tuple)...); + } + template + static Ret jsForwardHelperRet(Derived *obj, Func &func, Tuple const& tuple, std::index_sequence) { + return (obj->*func)(std::get(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 static JSValue toJsValue(JSContext *ctx, const std::optional &v) + { + if (!v) + return JS_UNDEFINED; + return makeJsString(ctx, *v); + } + + template + static JSValue jsForward(Func func, const char *name, JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) + { + try { + using Ret = typename FunctionTrait::Ret; + using Args = typename FunctionTrait::Args; + const auto args = getArgumentsHelper(Args(), ctx, name, argc, argv); + const auto obj = fromJsObject(ctx, this_val); + if constexpr (std::is_same_v) { + jsForwardHelperVoid(obj, func, args, std::make_index_sequence::value>()); + return JS_UNDEFINED; + } else { + return toJsValue(ctx, jsForwardHelperRet(obj, func, args, std::make_index_sequence>())); + } + } 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(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 { + static bool fromArg(JSContext *ctx, const char *, int, JSValue v) { + return JS_ToBool(ctx, v); + } +}; +template<> struct FromArgHelper { + 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 { + 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 { + 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 { + 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 { + 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 { + static QVariant fromArg(JSContext *ctx, const char *, int, JSValue v) { + return getJsVariant(ctx, v); + } +}; +template struct FromArgHelper { + static C *fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) { + C * const obj = JsExtension::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 +struct FunctionTrait { + using Ret = R; + using Args = PackHelper...>; +}; +template +struct FunctionTrait { + using Ret = R; + using Args = PackHelper...>; +}; + +} // 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 +#include + #include -#include #include -using InitializerMap = QMap; +using InitializerMap = QMap; 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 -#include +#include -QT_BEGIN_NAMESPACE -class QScriptEngine; -class QScriptValue; -QT_END_NAMESPACE +#include 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 #include +#include #include -#include #include #include #include @@ -53,28 +53,21 @@ #include #include #include - -#include +#include 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(reinterpret_cast(product)); - data.setProperty(ProductPtrKey, engine->newVariant(v)); - v.setValue(reinterpret_cast(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(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( + 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(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 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(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( - data.property(ProductPtrKey).toVariant().value()); - m_artifact = reinterpret_cast( - data.property(ArtifactPtrKey).toVariant().value()); - } + 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(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 ¶ms = 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 +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) { + product = attachedPointer(this_val, engine->productPropertyScriptClass()); + } else { + artifact = attachedPointer(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 +static JSValue js_moduleProperty(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +{ + try { + return moduleProperty(ctx, this_val, argc, argv); + } catch (const ErrorInfo &e) { + return throwError(ctx, e.toString()); + } +} + +template +static void initModuleProperties(ScriptEngine *engine, JSValue objectWithProperties) +{ + JSContext * const ctx = engine->context(); + JSValue func = JS_NewCFunction(ctx, js_moduleProperty, "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(&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(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(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(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(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(qscriptvalue_cast(ptrScriptValue)); - const ResolvedProduct *product = nullptr; - const Artifact *artifact = nullptr; - if (typeScriptValue.toString() == StringConstants::productValue()) { - QBS_ASSERT(ptr, return {}); - product = static_cast(ptr); - } else if (typeScriptValue.toString() == artifactType()) { - QBS_ASSERT(ptr, return {}); - artifact = static_cast(ptr); - product = artifact->product.get(); - } else { - return context->throwError(QScriptContext::TypeError, - QStringLiteral("Internal error: invalid type")); - } - - const auto qbsEngine = static_cast(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 #include -#include -#include +#include 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 #include -#include -#include - #include #include @@ -186,35 +183,39 @@ std::vector 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(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( - convertOptions(static_cast(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 #include -#include #include -#include - #include 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 { - 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 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 #include #include @@ -44,7 +46,6 @@ #include #include -#include #include #include #include @@ -53,133 +54,161 @@ #else #include #endif -#include -#include -#include namespace qbs { namespace Internal { -class Process : public QObject, public QScriptable, public ResourceAcquiringScriptObject +class Process : public JsExtension { - Q_OBJECT + friend class JsExtension; 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(ctx, "Process.exec", argc, argv); + bool throwOnError = false; + if (argc > 2) + throwOnError = fromArg(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(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 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(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(engine)->environment(); - } else { - t->m_environment - = QProcessEnvironment(*reinterpret_cast(v.value())); - } - se->setUsesIo(); - - return obj; -} - -Process::Process(QScriptContext *context) -{ - Q_UNUSED(context); - Q_ASSERT(thisObject().engine() == engine()); - - m_qProcess = std::make_unique(); - 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(v.value())); + } + 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(); + 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(ctx, "Process.shellQuote", argc, argv); + HostOsInfo::HostOs hostOs = HostOsInfo::hostOs(); + if (argc > 2) { + const auto osList = fromArg(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 +#include -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 - -#include -#include -#include - -#include -#include - -#include - -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 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 +#include #include #include -#include #include #include #include -#include -#include -#include - -// 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 { 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(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 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(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()) -{ - 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(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(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(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(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(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(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 PropertyList::format() const { - Q_ASSERT(thisObject().engine() == engine()); - auto p = qscriptvalue_cast(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(thisObject()); - return p->engine()->toScriptValue(p->d->propertyListObject); -} - QString PropertyList::toString(const QString &plistFormat) const { - Q_ASSERT(thisObject().engine() == engine()); - auto p = qscriptvalue_cast(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 #include -#include #include #include -#include -#include -#include - namespace qbs { namespace Internal { @@ -60,63 +57,49 @@ static bool tempDirIsCanonical() return false; } -class TemporaryDir : public QObject, public QScriptable +class TemporaryDir : public JsExtension { - Q_OBJECT + friend class JsExtension; 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(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 #include #include #include #include -#include #include #include @@ -53,16 +54,12 @@ #include #endif -#include -#include -#include - namespace qbs { namespace Internal { -class TextFile : public QObject, public QScriptable, public ResourceAcquiringScriptObject +class TextFile : public JsExtension { - Q_OBJECT + friend class JsExtension; 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 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(context->argument(1).toInt32()) - ); - break; - case 3: - t = new TextFile(context, - context->argument(0).toString(), - static_cast(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(engine); - se->addResourceAcquiringScriptObject(t); - const DubiousContextList dubiousContexts({ +JSValue TextFile::ctor(JSContext *ctx, JSValueConst, JSValueConst, + int argc, JSValueConst *argv, int) +{ + try { + const auto filePath = getArgument(ctx, "TextFile constructor", argc, argv); + OpenMode mode = ReadOnly; + QString codec = QLatin1String("UTF-8"); + if (argc > 1) { + mode = static_cast + (fromArg(ctx, "TextFile constructor", 2, argv[1])); + } + if (argc > 2) { + codec = fromArg(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(filePath); +TextFile::TextFile(JSContext *, const QString &filePath, OpenMode mode, const QString &codec) +{ + auto file = std::make_unique(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 -#include #include #include #include @@ -84,9 +86,6 @@ struct fat_arch_64 { #include #include -#include -#include - 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 { - 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(ctx, "Utilities.canonicalPlatform", argc, argv); + if (platform.isEmpty()) + return makeJsStringList(ctx, {}); + std::vector 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(ctx, "Utilities.canonicalTargetArchitecture", + argc, argv); + QString endianness, vendor, system, abi; + if (argc > 1) + endianness = fromArg(ctx, "Utilities.canonicalTargetArchitecture", 2, argv[1]); + if (argc > 2) + vendor = fromArg(ctx, "Utilities.canonicalTargetArchitecture", 3, argv[2]); + if (argc > 3) + system = fromArg(ctx, "Utilities.canonicalTargetArchitecture", 4, argv[3]); + if (argc > 4) + abi = fromArg(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(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(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(ctx, "Utilities.cStringQuote", argc, argv); + return makeJsString(ctx, escapedString( + reinterpret_cast(str.constData()), str.size())); + } catch (const QString &error) { + return throwError(ctx, error); } - QString value = context->argument(0).toString(); - return engine->toScriptValue(escapedString(reinterpret_cast(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(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(ctx, "Utilities.getNativeSetting", argc, argv); - QString key = context->argumentCount() > 1 ? context->argument(1).toString() : QString(); + QString key; + if (argc > 1) + key = fromArg(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(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(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(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(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(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 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(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(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(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(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(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(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(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 -#include - -#include -#include - -namespace qbs { -namespace Internal { - -class Evaluator; -class Item; - -class EvaluationData -{ -public: - Evaluator *evaluator = nullptr; - const Item *item = nullptr; - mutable QHash 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 + #include #include #include #include +#include #include #include #include @@ -61,27 +62,61 @@ namespace qbs { namespace Internal { +class EvaluationData +{ +public: + Evaluator *evaluator = nullptr; + const Item *item = nullptr; + mutable QHash 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(data); - delete m_scriptClass; + Set valuesToFree; + for (const auto &data : qAsConst(m_scriptValueMap)) { + const auto evalData = attachedPointer(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 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(m_scriptValueMap.value(item)); - if (data) + const auto data = attachedPointer(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 &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 &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(obj, evaluator->classId()); + if (!data) + return -1; + const Item::PropertyMap &map = data->item->properties(); + *plen = map.size(); + if (!map.isEmpty()) { + *ptab = reinterpret_cast(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 &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 &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 createExtraScope(const JSSourceValue *value, Item *outerItem, + JSValue *outerScriptValue) + { + std::pair 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 ¤tNextChain = data->evaluator->currentNextChain(); + Set 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(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(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 +#include -#include +#include #include #include +#include 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 &requestedProperties() { return m_requestedProperties; } + Set ¤tNextChain() { 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 m_scriptValueMap; + ScriptEngine * const m_scriptEngine; + const JSClassID m_scriptClass; + mutable QHash m_scriptValueMap; mutable QHash m_fileContextScopesMap; + QString m_pathPropertiesBaseDir; + PropertyDependencies m_propertyDependencies; + std::stack m_requestedProperties; + Set m_currentNextChain; + bool m_valueCacheEnabled = false; }; -void throwOnEvaluationError(ScriptEngine *engine, const QScriptValue &scriptValue, +void throwOnEvaluationError(ScriptEngine *engine, const std::function &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 -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include - -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(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 createExtraScope(const JSSourceValue *value, Item *outerItem, - QScriptValue *outerScriptValue) - { - std::pair 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(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 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(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(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 &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 &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(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 m_it; -}; - -QScriptClassPropertyIterator *EvaluatorScriptClass::newIterator(const QScriptValue &object) -{ - auto const data = attachedPointer(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 - -#include - -#include - -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 m_currentNextChain; - PropertyDependencies m_propertyDependencies; - std::stack 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 #include -#include - #include #include #include 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 #include -#include +#include #include #include @@ -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 #include -#include 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 #include #include -#include #include #include @@ -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 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 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 #include +#include #include #include @@ -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(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 #include -#include - 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(object); + const Artifact * const artifact = attachedPointer(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 +#include + #include #include @@ -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 m_projectObjectIds; std::unordered_map> m_parameterObjects; std::unordered_map m_exportsObjectIds; - Set m_importIds; + Set m_importIds; Set 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 #include #include +#include #include +#include + 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; + using ProbeProperty = std::pair; std::vector 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 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 #include +#include + #include #include @@ -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 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 +#include #include #include #include #include #include #include +#include #include #include #include @@ -63,18 +65,15 @@ #include #include -#include -#include - +#include #include #include #include +#include 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::create(Logger &logger, EvalContext evalContext) { - std::lock_guard lock(m_creationDestructionMutex); return std::make_unique(logger, evalContext, PrivateTag()); } -ScriptEngine::~ScriptEngine() +ScriptEngine *ScriptEngine::engineForRuntime(const JSRuntime *runtime) +{ + return static_cast(JS_GetRuntimeOpaque(const_cast(runtime))); + +} + +ScriptEngine *ScriptEngine::engineForContext(const JSContext *ctx) +{ + return engineForRuntime(JS_GetRuntime(const_cast(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 &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(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(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(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(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(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 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 JSValue getScriptValue(JSContext *ctx, const T *t, + const std::unordered_map &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 void setScriptValue(JSContext *ctx, const T *t, JSValue value, + std::unordered_map &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 &propGetter) +{ + std::lock_guard lock(m_artifactsMutex); + const Artifact * const a = attachedPointer(obj, dataWithPtrClass()); + return a ? propGetter(a) : JS_EXCEPTION; } void ScriptEngine::addCanonicalFilePathResult(const QString &filePath, @@ -617,49 +742,100 @@ Set 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(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 &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(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 #include #include +#include #include #include #include +#include #include #include @@ -56,8 +58,7 @@ #include #include -#include - +#include #include #include #include @@ -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; - -/* - * 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 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 propertiesRequestedFromArtifact() const { return m_propertiesRequestedFromArtifact; @@ -167,7 +161,7 @@ public: RequestedArtifacts requestedArtifacts() const { return m_requestedArtifacts; } Set requestedExports() const { return m_requestedExports; } - void addImportRequestedInScript(qint64 importValueId); + void addImportRequestedInScript(quintptr importValueId); std::vector 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 fileLastModifiedResults() const { return m_fileLastModifiedResult; } Set 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 = std::enable_if_t>, - typename = std::enable_if_t>> - > QScriptValue newFunction(QScriptValue (*signature)(QScriptContext *, E, T), T arg) { - return QScriptEngine::newFunction( - reinterpret_cast(signature), - reinterpret_cast(const_cast< - std::add_pointer_t< - std::remove_const_t< - std::remove_pointer_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 &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 &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 m_jsImportCache; - std::unordered_map 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 m_jsImportCache; + std::unordered_map m_jsFileCache; + bool m_propertyCacheEnabled = true; + bool m_active = false; + bool m_canceling = false; QHash m_propertyCache; PropertySet m_propertiesRequestedInScript; QHash m_propertiesRequestedFromArtifact; Logger &m_logger; - QScriptValue m_definePropertyFunction; - QScriptValue m_emptyFunction; QProcessEnvironment m_environment; QHash m_canonicalFilePathResult; QHash m_fileExistsResult; @@ -342,28 +355,36 @@ private: QHash m_fileLastModifiedResult; std::stack m_currentDirPathStack; std::stack m_extensionSearchPathsStack; - QScriptValue m_loadFileFunction; - QScriptValue m_loadExtensionFunction; - QScriptValue m_requireFunction; - QScriptValue m_qbsObject; - QScriptValue m_consoleObject; - QScriptValue m_cancelationError; + std::stack> m_evalPositions; + JSValue m_qbsObject = JS_UNDEFINED; qint64 m_elapsedTimeImporting = -1; bool m_usesIo = false; EvalContext m_evalContext; - std::vector m_resourceAcquiringScriptObjects; const std::unique_ptr m_observer; - std::vector> m_observedProperties; - std::vector m_requireResults; - std::unordered_map> m_filePathsPerImport; + std::vector> m_observedProperties; + JSValueList m_requireResults; + std::unordered_map> m_filePathsPerImport; std::vector m_importsRequestedInScript; Set m_productsWithRequestedDependencies; RequestedArtifacts m_requestedArtifacts; Set m_requestedExports; ObserveMode m_observeMode = ObserveMode::Disabled; - std::unordered_map m_productScriptValues; - std::unordered_map m_projectScriptValues; - std::unordered_map m_moduleScriptValues; + std::unordered_map m_baseProductScriptValues; + std::unordered_map m_productArtifactsMapScriptValues; + std::unordered_map m_moduleArtifactsMapScriptValues; + std::unordered_map m_projectScriptValues; + std::unordered_map m_baseModuleScriptValues; + QList m_scopeChains; + JSValueList m_contextStack; + QHash m_exoticMethods; + QHash m_classes; + QHash m_internalExtensions; + QHash m_stringCache; + QHash m_evalResults; + QHash, 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 #include -#include - 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 +#include -#include +#include 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 + #include 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(""))) + 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 +#include +#include -#include +#include 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 &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(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 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 +#include "codelocation.h" +#include "porting.h" +#include "qbs_export.h" +#include + +#include #include #include -#include -#include -#include +#include +#include namespace qbs { +class ErrorInfo; namespace Internal { +class ScriptEngine; -template -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; -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 handleJsProperties(JSContext *ctx, JSValueConst obj, const PropertyHandler &handler); +inline quintptr jsObjectId(const JSValue &val) { return quintptr(JS_VALUE_GET_OBJ(val)); } template -void attachPointerTo(QScriptValue &scriptValue, T *ptr) +void attachPointerTo(JSValue &scriptValue, T *ptr) { - QVariant v; - v.setValue(reinterpret_cast(ptr)); - scriptValue.setData(scriptValue.engine()->newVariant(v)); + JS_SetOpaque(scriptValue, const_cast *>(ptr)); } template -T *attachedPointer(const QScriptValue &scriptValue) +T *attachedPointer(const JSValue &scriptValue, JSClassID id) { - const auto ptr = scriptValue.data().toVariant().value(); - return reinterpret_cast(ptr); + return reinterpret_cast(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 -#include +#include +#include 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 index 2c1ffc66b..000000000 --- a/src/shared/qtscript +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2c1ffc66bf5d5db05018d7b06253b5ca51e557ab 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 +#include +#include +#include + +#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 +#include + +#if defined(_MSC_VER) +#include +typedef SSIZE_T ssize_t; +#else +#include +#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 +#include +#include +#include +#include +#include + +#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(""); + 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 + +#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 + +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 +#include +#include +#include +#include + +#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 + +#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 +#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_, "") +DEF(_ret_, "") +DEF(_var_, "") +DEF(_arg_var_, "") +DEF(_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, "") +DEF(computed_field, "") +DEF(static_computed_field, "") /* must come after computed_fields */ +DEF(class_fields_init, "") +DEF(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, "") +/* 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 +#include +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#endif +#include +#include +#include +#if defined(__APPLE__) +#include +#elif defined(__linux__) +#include +#elif defined(__FreeBSD__) +#include +#endif + +#ifdef _MSC_VER +#include +#include +#endif + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ + || defined(__APPLE__) +#include +#else +#include +#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 +#include +#include +#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(""); + 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, ""); + } 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 = ""; + 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 : ""); + 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 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 `